package cn.**.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @ProjectName: springbootSecurity * @Package: cn.cn.***.security.controller * @Author: huat * @Date: 2019/12/12 14:56 * @Version: 1.0 */ @RestController public class UserController { @RequestMapping("/index") public String login(){ return "index"; } @RequestMapping("test") public String test(){ return "hello test"; } }
package cn.**.service; import cn.**.dao.UserDao; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; /** * @ProjectName: springbootSecurity * @Package: cn.cn.**.utile * @Author: huat * @Date: 2019/12/12 14:48 * @Version: 1.0 */ @Service//將這個類注入到spring容器中 public class UserServiceImpl implements UserService { @Autowired private UserDao userDao; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { return userDao.getUser(s); }
package cn.**.dao; import cn.**.entity.AuthorityUser; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; /** * @ProjectName: springbootSecurity * @Package: cn.cn.**.security.dao * @Author: huat * @Date: 2019/12/12 15:04 * @Version: 1.0 */ @Mapper public interface UserDao { /** * 根據用戶名查詢角色 * @param username 用戶名 * @return */ AuthorityUser getUser(@Param("username") String username); /** * 添加用戶 * @param username 用戶名 * @param password 密碼 * @return */ int saveUser(@Param("username") String username, @Param("password") String password); /** * 保存token * @param id * @param token * @return */ int updateUserToken(@Param("id")int id,@Param("token")String token); /** * 根據用戶id返回token * @param id * @return 用戶token */ String getTokenById(@Param("id")int id); }
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="cn.**.dao.UserDao"> <resultMap id="userRole" type="authorityUser"> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="password" property="password"></result> <collection property="authorityRoles" ofType="authorityRole"> <id column="rid" property="rid"></id> <result property="roleName" column="role_name"></result> <result property="roleNameCN" column="role_name_CN"></result> </collection> </resultMap> <select id="getUser" resultMap="userRole"> select u.id id,u.password password, u.username username,r.id rid,r.role_name,r.role_name_CN from authority_user u left join authority_user_role ur on ur.user_id=u.id left join authority_role r on ur.role_id=r.id where username=#{username} </select> <insert id="saveUser"> insert into authority_user(username,password) values(#{username},#{password}) </insert> <update id="updateUserToken"> update authority_user set token=#{token} where id=#{id} </update> <select id="getTokenById" resultType="String"> select token from authority_user where id=#{id} </select> </mapper>
package cn.**.entity; import com.fasterxml.jackson.annotation.JsonIgnore; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; /** * @ProjectName: springbootSecurity * @Package: cn.cn.**.security.entity * @Author: huat * @Date: 2019/12/12 15:12 * @Version: 1.0 * 建立實體類第一種方式 */ public class AuthorityUser implements UserDetails { private int id; private String username; private String password; private List<AuthorityRole> authorityRoles; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public List<AuthorityRole> getAuthorityRoles() { return authorityRoles; } public void setAuthorityRoles(List<AuthorityRole> authorityRoles) { this.authorityRoles = authorityRoles; } @Override @JsonIgnore//忽略此屬性 轉成json字符串時不進行轉換 public Collection<? extends GrantedAuthority> getAuthorities() { return authorityRoles; } @Override @JsonIgnore//忽略此屬性 轉成json字符串時不進行轉換 public boolean isAccountNonExpired() { return true; } @Override @JsonIgnore//忽略此屬性 轉成json字符串時不進行轉換 public boolean isAccountNonLocked() { return true; } @Override @JsonIgnore//忽略此屬性 轉成json字符串時不進行轉換 public boolean isCredentialsNonExpired() { return true; } /** * 當前用戶是否可用 * @return */ @Override @JsonIgnore//忽略此屬性 轉成json字符串時不進行轉換 public boolean isEnabled() { return true; } }
package cn.**.entity; import com.fasterxml.jackson.annotation.JsonIgnore; import org.springframework.security.core.GrantedAuthority; /** * @ProjectName: springbootSecurity * @Package: cn.cn.**.security.entity * @Author: huat * @Date: 2019/12/12 16:14 * @Version: 1.0 */ public class AuthorityRole implements GrantedAuthority { private int rid; private String roleName; private String roleNameCN; public int getRid() { return rid; } public void setRid(int rid) { this.rid = rid; } public String getRoleName() { return roleName; } public void setRoleName(String roleName) { this.roleName = roleName; } public String getRoleNameCN() { return roleNameCN; } public void setRoleNameCN(String roleNameCN) { this.roleNameCN = roleNameCN; } @JsonIgnore//忽略此屬性 轉成json字符串時不進行轉換 @Override public String getAuthority() { return roleName; } }
package cn.**.entity; import java.util.Date; /** * @ProjectName: springSecuritySeparate * @Package: cn.**.entity * @Author: huat * @Date: 2019/12/24 13:13 * @Version: 1.0 */ public class Payload<T> { private String id; private T userInfo; private Date expiration; public String getId() { return id; } public void setId(String id) { this.id = id; } public T getUserInfo() { return userInfo; } public void setUserInfo(T userInfo) { this.userInfo = userInfo; } public Date getExpiration() { return expiration; } public void setExpiration(Date expiration) { this.expiration = expiration; } }
package cn.**.filter; import cn.**.dao.UserDao; import cn.**.entity.AuthorityRole; import cn.**.entity.AuthorityUser; import cn.**.util.JwtUtil; import cn.**.util.RsaKeyProperties; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @ProjectName: springSecuritySeparate * @Package: cn.**.filter * @Author: huat * @Date: 2019/12/24 17:04 * @Version: 1.0 */ public class JwtTokenFilter extends UsernamePasswordAuthenticationFilter { private AuthenticationManager authentication; private RsaKeyProperties rsaKeyProperties; private UserDao userDao; public JwtTokenFilter(AuthenticationManager authentication, RsaKeyProperties rsaKeyProperties,UserDao userDao) { this.authentication = authentication; this.rsaKeyProperties = rsaKeyProperties; this.userDao=userDao; } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { try { //從ajax請求流讀取參數,轉換成AuthorityUser實體類 AuthorityUser authorityUser= new ObjectMapper().readValue(request.getInputStream(), AuthorityUser.class); //參數是前臺傳過來的 UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(authorityUser.getUsername(), authorityUser.getPassword()); return authentication.authenticate(authRequest); } catch (IOException e) { e.printStackTrace(); try { response.setContentType("application/json;charset=utf-8"); //返回401狀態碼 權限不足 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); //響應流 PrintWriter out=response.getWriter(); //封裝返回數據 Map<String,Object> resultMap=new HashMap<String,Object>(); resultMap.put("code",HttpServletResponse.SC_UNAUTHORIZED); resultMap.put("data",""); resultMap.put("msg","用戶名密碼錯誤"); //new ObjectMapper().writeValueAsString(resultMap)將map轉成json out.write(new ObjectMapper().writeValueAsString(resultMap)); out.flush(); out.close(); }catch (Exception outEX){ outEX.printStackTrace(); } throw new RuntimeException(e); } } @Override public void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { AuthorityUser user=(AuthorityUser)authResult.getPrincipal(); /* user.setId(((AuthorityUser)authResult.getPrincipal()).getId()); user.setUsername(authResult.getName()); user.setAuthorityRoles((List<AuthorityRole>) authResult.getAuthorities());*/ String token= JwtUtil.generateTokenExpireInMinutes(user,rsaKeyProperties.getPrivateKey(),24 * 60); response.setHeader("Authorization","bearer "+token); try { //將token保存到數據庫當中 int reslut=userDao.updateUserToken(((AuthorityUser)authResult.getPrincipal()).getId(),token); //若是保存成功給用戶返回 if(reslut>0){ response.setContentType("application/json;charset=utf-8"); //返回200狀態碼 response.setStatus(HttpServletResponse.SC_OK); //響應流 PrintWriter out=response.getWriter(); //封裝返回數據 Map<String,Object> resultMap=new HashMap<String,Object>(); resultMap.put("code",HttpServletResponse.SC_OK); resultMap.put("data",""); resultMap.put("msg","認證經過"); //new ObjectMapper().writeValueAsString(resultMap)將map轉成json out.write(new ObjectMapper().writeValueAsString(resultMap)); out.flush(); out.close(); } }catch (Exception outEX){ outEX.printStackTrace(); userDao.updateUserToken(((AuthorityUser)authResult.getPrincipal()).getId(),""); } } }
package cn.**.filter; import cn.**.entity.AuthorityUser; import cn.**.entity.Payload; import cn.**.util.JwtUtil; import cn.**.util.RsaKeyProperties; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; /** * @ProjectName: springSecuritySeparate * @Package: cn.**.filter * @Author: huat * @Date: 2019/12/24 18:03 * @Version: 1.0 */ public class JwtVerifyFileter extends BasicAuthenticationFilter { private RsaKeyProperties rsaKeyProperties; public JwtVerifyFileter(AuthenticationManager authenticationManager, RsaKeyProperties rsaKeyProperties) { super(authenticationManager); this.rsaKeyProperties = rsaKeyProperties; } @Override public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { // request.getHeader("Authorization")這個括號中的值須要和 // JwtTokenFilter這個類中successfulAuthentication方法中 // response.setHeader("Authorization","bearer "+token);的括號中的第一個值一致 String header = request.getHeader("Authorization"); // header.startsWith("bearer ")這個括號中的值須要和 // JwtTokenFilter這個類中successfulAuthentication方法中 // response.setHeader("Authorization","bearer "+token);的括號中的第二個值加號前一致 if (header == null || !header.startsWith("bearer ")) { //若是沒有登錄提示用戶進行登錄 response.setContentType("application/json;charset=utf-8"); //返回403狀態碼 response.setStatus(HttpServletResponse.SC_FORBIDDEN); //響應流 PrintWriter out = response.getWriter(); //封裝返回數據 Map<String, Object> resultMap = new HashMap<String, Object>(); resultMap.put("code", HttpServletResponse.SC_FORBIDDEN); resultMap.put("data", ""); resultMap.put("msg", "請登陸"); //new ObjectMapper().writeValueAsString(resultMap)將map轉成json out.write(new ObjectMapper().writeValueAsString(resultMap)); out.flush(); out.close(); chain.doFilter(request, response); } else { //獲取token String token=header.replace("bearer ",""); //驗證token是否正確 Payload<AuthorityUser> payload=JwtUtil.getInfoFromToken(token,rsaKeyProperties.getPublicKey(), AuthorityUser.class); AuthorityUser user=payload.getUserInfo(); if(null!=user){ UsernamePasswordAuthenticationToken authResult=new UsernamePasswordAuthenticationToken(user.getUsername(),null,user.getAuthorities()); SecurityContextHolder.getContext().setAuthentication(authResult); chain.doFilter(request, response); } } } }
package cn.**.util; import cn.**.entity.AuthorityRole; import cn.**.entity.AuthorityUser; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import lombok.extern.slf4j.Slf4j; import org.apache.catalina.User; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * @ProjectName: springSecuritySeparate * @Package: cn.**.util * @Author: huat * @Date: 2019/12/24 10:58 * @Version: 1.0 */ @Slf4j public class JsonUtil { public static final ObjectMapper objectMapper=new ObjectMapper(); /** * 將對象轉爲json * @param object 對象 * @return json */ public static String toString(Object object) { if(null==object){ return null; } if(object.getClass()==String.class){ return (String)object; } try { return objectMapper.writeValueAsString(object); } catch (JsonProcessingException e) { log.info(object+"序列化出錯"+e.getMessage()); e.printStackTrace(); return null; } } /** * 將json轉換成對象 * @param json json字符串 * @param tClass 類 * @param <T> 泛型 * @return 返回對象 */ public static <T> T toBean(String json,Class<T> tClass){ try { return objectMapper.readValue(json,tClass); } catch (IOException e) { log.info(json+"轉對象失敗"+e.getMessage()); e.printStackTrace(); return null; } } /** * 將json轉換成集合對象 * @param json json字符串 * @param eClass 類 集合對象 * @param <E> 泛型 * @return 返回對象 */ public static <E> List<E> toList(String json,Class<E> eClass){ try { return objectMapper.readValue(json,objectMapper.getTypeFactory().constructCollectionType(List.class,eClass)); } catch (IOException e) { log.info(json+"轉對象失敗"+e.getMessage()); e.printStackTrace(); return null; } } /** * 將json轉換成Map對象 * @param json json字符串 * @param kClass 鍵對象 * @param vClass 值對象 * @param <K> 鍵的類型 * @param <V> 值的類型 * @return map對象 */ public static <K,V> Map<K,V> toMap(String json,Class<K> kClass,Class<V> vClass){ try { return objectMapper.readValue(json,objectMapper.getTypeFactory().constructMapType(Map.class,kClass,vClass)); } catch (IOException e) { log.info(json+"轉對象失敗"+e.getMessage()); e.printStackTrace(); return null; } } /** * 複雜類型轉換 泛型套泛型 * @param json json字符串 * @param type 返回類型 * @param <T> * @return 返回對象 */ public static <T> T nativeRead(String json, TypeReference<T> type){ try { return objectMapper.readValue(json,type); } catch (IOException e) { log.info(json+"轉對象失敗"+e.getMessage()); e.printStackTrace(); return null; } } public static void main(String[] args) { //測試nativeRead方法 String json="{\"id\":1,\"username\":\"張三\",\"password\":\"123\",\"authorityRoles\":[{\"rid\":0,\"roleName\":\"張三0\",\"roleNameCN\":null},{\"rid\":1,\"roleName\":\"張三1\",\"roleNameCN\":null},{\"rid\":2,\"roleName\":\"張三2\",\"roleNameCN\":null},{\"rid\":3,\"roleName\":\"張三3\",\"roleNameCN\":null},{\"rid\":4,\"roleName\":\"張三4\",\"roleNameCN\":null}]}\n"; JsonUtil.nativeRead(json,new TypeReference<AuthorityUser>(){}); } }
package cn.**.util; import cn.**.entity.Payload; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.joda.time.DateTime; import java.security.PrivateKey; import java.security.PublicKey; import java.util.Base64; import java.util.UUID; /** * @ProjectName: springSecuritySeparate * @Package: cn.**.util * @Author: huat * @Date: 2019/12/24 12:44 * @Version: 1.0 */ public class JwtUtil { private final static String JWT_PAYLOAD_USER_KEY = "user"; /** * 私鑰加密token * * @param userInfo 載荷中的數據 * @param privateKey 私鑰 * @param expire 過時時間 單位分鐘 * @return */ public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expire) { return Jwts.builder() .claim(JWT_PAYLOAD_USER_KEY, JsonUtil.toString(userInfo)) .setId(createJTI()) .setExpiration(DateTime.now().plusMinutes(expire).toDate()) .signWith(privateKey, SignatureAlgorithm.RS256) .compact(); } /** * 私鑰加密token * * @param userInfo 載荷中的數據 * @param privateKey 私鑰 * @param expire 過時時間 單位秒 * @return */ public static String generateTokenExpireInSeconds(Object userInfo, PrivateKey privateKey, int expire) { return Jwts.builder() .claim(JWT_PAYLOAD_USER_KEY, JsonUtil.toString(userInfo)) .setId(createJTI()) .setExpiration(DateTime.now().plusSeconds(expire).toDate()) .signWith(privateKey, SignatureAlgorithm.RS256) .compact(); } private static String createJTI() { return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes())); } /** * 公鑰解析token * @param token 用戶請求的令牌 * @param publicKey 公鑰 * @return */ private static Jws<Claims> parserToken(String token, PublicKey publicKey){ return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token); } /** * 獲取token中的載荷信息 * @param token 用戶請求的令牌 * @param publicKey 公鑰 * @param userType 用戶類型 * @param <T> * @return 用戶信息 */ public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) { Jws<Claims> claimsJws = parserToken(token, publicKey); Claims body = claimsJws.getBody(); Payload<T> payload = new Payload<T>(); payload.setId(body.getId()); payload.setUserInfo(JsonUtil.toBean(body.get(JWT_PAYLOAD_USER_KEY).toString(),userType)); payload.setExpiration(body.getExpiration()); return payload; } /** * 獲取token中的載荷信息 * @param token 用戶請求的令牌 * @param publicKey 公鑰 * @param <T> * @return 用戶信息 */ public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey) { Jws<Claims> claimsJws = parserToken(token, publicKey); Claims body = claimsJws.getBody(); Payload<T> payload = new Payload<T>(); payload.setId(body.getId()); payload.setExpiration(body.getExpiration()); return payload; } }
package cn.**.util; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.io.IOException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.PublicKey; import java.security.spec.InvalidKeySpecException; /** * @ProjectName: springSecuritySeparate * @Package: cn.**.util * @Author: huat * @Date: 2019/12/24 15:58 * @Version: 1.0 */ @Configuration @ConfigurationProperties("rsa.key")//從配置文件中獲取頭信息是rsa.key的配置 public class RsaKeyProperties { private String publicKeyFile; private String privateKeyFile; private PublicKey publicKey; private PrivateKey privateKey; @PostConstruct//在初始化完成以後執行此方法 public void createRsaKey() throws InvalidKeySpecException, NoSuchAlgorithmException, IOException { publicKey=RsaUtil.getPublicKey(publicKeyFile); privateKey=RsaUtil.getPrivateKey(privateKeyFile); } public String getPublicKeyFile() { return publicKeyFile; } public void setPublicKeyFile(String publicKeyFile) { this.publicKeyFile = publicKeyFile; } public String getPrivateKeyFile() { return privateKeyFile; } public PublicKey getPublicKey() { return publicKey; } public void setPublicKey(PublicKey publicKey) { this.publicKey = publicKey; } public PrivateKey getPrivateKey() { return privateKey; } public void setPrivateKey(PrivateKey privateKey) { this.privateKey = privateKey; } public void setPrivateKeyFile(String privateKeyFile) { this.privateKeyFile = privateKeyFile; } }
package cn.**.util; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.nio.file.Files; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; /** * @ProjectName: springSecuritySeparate * @Package: cn.**.util * @Author: huat * @Date: 2019/12/24 13:29 * @Version: 1.0 */ public class RsaUtil { private final static int DEFAULT_KEY_SIZE=2048; /** * 根據密文,生成rea公鑰和私鑰,並寫入指定文件 * @param publicKeyFileName 公鑰文件路徑 * @param privateKeyFileName 私鑰文件路徑 * @param secret 鹽 * @param keySize 大小 */ public static void generateKey(String publicKeyFileName,String privateKeyFileName,String secret, int keySize) throws NoSuchAlgorithmException, IOException { KeyPairGenerator keyPairGenerator=KeyPairGenerator.getInstance("RSA"); SecureRandom secureRandom=new SecureRandom(secret.getBytes()); keyPairGenerator.initialize(Math.max(keySize,DEFAULT_KEY_SIZE),secureRandom); KeyPair keyPair=keyPairGenerator.genKeyPair(); //獲取公鑰並寫出 byte[] publicKeyByte=keyPair.getPublic().getEncoded(); publicKeyByte= Base64.getEncoder().encode(publicKeyByte); writeFile(publicKeyFileName,publicKeyByte); //獲取公鑰並寫入 byte[] privateKeyByte=keyPair.getPrivate().getEncoded(); privateKeyByte= Base64.getEncoder().encode(privateKeyByte); writeFile(privateKeyFileName,privateKeyByte); } /** * 獲取祕鑰 * @param bytes 祕鑰的字節形式 * @return * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException */ private static PrivateKey getPrivateKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException { bytes=Base64.getDecoder().decode(bytes); PKCS8EncodedKeySpec spec=new PKCS8EncodedKeySpec(bytes); KeyFactory keyFactory=KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(spec); } /** * 獲取公鑰 * @param bytes 公鑰的字節形式 * @return * @throws NoSuchAlgorithmException * @throws InvalidKeySpecException */ private static PublicKey getPublicKey(byte[] bytes) throws NoSuchAlgorithmException, InvalidKeySpecException { bytes=Base64.getDecoder().decode(bytes); X509EncodedKeySpec spec=new X509EncodedKeySpec(bytes); KeyFactory keyFactory=KeyFactory.getInstance("RSA"); return keyFactory.generatePublic(spec); } /** * 從文件中讀取公鑰 * @param fileName 公鑰保存路徑 相對於classpath * @return 公鑰對象 * @throws InvalidKeySpecException * @throws NoSuchAlgorithmException */ public static PublicKey getPublicKey(String fileName) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException { byte[] bytes=readFile(fileName); return getPublicKey(bytes); } /** * 從文件中讀取公鑰 * @param fileName 公鑰保存路徑 相對於classpath * @return 私鑰對象 * @throws InvalidKeySpecException * @throws NoSuchAlgorithmException */ public static PrivateKey getPrivateKey(String fileName) throws InvalidKeySpecException, NoSuchAlgorithmException, IOException { byte[] bytes=readFile(fileName); return getPrivateKey(bytes); } private static byte[] readFile(String fileName) throws IOException { return Files.readAllBytes(new File(fileName).toPath()); } private static void writeFile(String fileName,byte[] bytes) throws IOException { File file=new File(fileName); if(!file.exists()){ file.createNewFile(); } FileOutputStream fos = new FileOutputStream( fileName); fos.write(bytes); fos.close(); } }
package cn.**.util; import cn.**.filter.JwtTokenFilter; import cn.**.filter.JwtVerifyFileter; import cn.**.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.cors.CorsConfiguration; import org.springframework.web.cors.CorsConfigurationSource; import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.Arrays; /** * @ProjectName: springbootSecurity * @Package: cn.cn.**.security.util * @Author: huat * @Date: 2019/12/14 8:06 * @Version: 1.0 */ /** * 開啓security註解支持 * * @EnableWebSecurity EnableGlobalMethodSecurity 方法級受權 * (securedEnabled=true) 開啓@Secured 註解過濾權限 * (jsr250Enabled=true)開啓@RolesAllowed 註解過濾權限 * (prePostEnabled=true) 使用表達式時間方法級別的安全性 4個註解可用 * @EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled=true,jsr250Enabled=true) */ @Configuration @EnableWebSecurity public class SpringSercurityConfig extends WebSecurityConfigurerAdapter { /* @Autowired UserService1 userService;*/ @Autowired UserService userService; @Autowired private RsaKeyProperties rsaKeyProperties; /** * 將security中加密方式注入到spring容器中 * * @return */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 解決跨域 * @return */ @Bean public CorsConfigurationSource corsConfigurationSource() { final CorsConfiguration configuration = new CorsConfiguration(); //指定容許跨域的請求(*全部):http://wap.ivt.guansichou.com configuration.setAllowedOrigins(Arrays.asList("*")); configuration.setAllowedMethods(Arrays.asList("HEAD", "GET", "POST", "PUT", "DELETE", "PATCH")); // setAllowCredentials(true) is important, otherwise: // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. configuration.setAllowCredentials(true); // setAllowedHeaders is important! Without it, OPTIONS preflight request // will fail with 403 Invalid CORS request configuration.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "X-User-Agent", "Content-Type")); final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", configuration); return source; } /** * 將帳號密碼設置在內存當中 * * @param auth * @throws Exception */ @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth //將UserDetailsService放到容器中 .userDetailsService(userService) //加密方式放入 .passwordEncoder(passwordEncoder()); } //先後端分離時候使用 @Override public void configure(HttpSecurity http) throws Exception { //釋放靜態資源,指定資源攔截規則, // 指定自定義認證頁面,指定退出認證配置,csrf(跨域僞造請求)配置 http .csrf() .disable()//關閉csrf(跨域僞造請求) .cors() .and().servletApi().disable() .requestCache().disable() .authorizeRequests() .antMatchers("/index").hasAnyRole("ADMIN") .antMatchers("/test").hasRole("TEST") .anyRequest().authenticated()//其餘資源須要認證 .and() .addFilter(new JwtTokenFilter(super.authenticationManager(),rsaKeyProperties)) .addFilter(new JwtVerifyFileter(super.authenticationManager(),rsaKeyProperties)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//禁用session ; } }
package cn.**; import cn.**.util.RsaKeyProperties; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.EnableConfigurationProperties; /** * @ProjectName: springSecurity * @Package: cn.cn.** * @Author: huat * @Date: 2019/12/21 8:38 * @Version: 1.0 */ @SpringBootApplication @EnableConfigurationProperties(RsaKeyProperties.class)//加載RsaKeyProperties類,以bean形式注入到spring容器中 public class SpringBootApplicationStart { public static void main(String[] args) { SpringApplication.run(SpringBootApplicationStart.class,args); } }
spring: datasource: driver-class-name: com.mysql.jdbc.Driver username: root password: root url: jdbc:mysql://localhost:3306/security?characterEncoding=UTF-8 mybatis: type-aliases-package: cn.**.entity logging: level: cn.**.dao: debug rsa: key: #公鑰保存地方 publicKeyFile: E:\test\public.pub #私鑰保存地方 privateKeyFile: E:\test\private
package cn.**.filter; import cn.**.dao.UserDao; import cn.**.entity.AuthorityUser; import cn.**.entity.Payload; import cn.**.util.JwtUtil; import cn.**.util.RsaKeyProperties; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.PrintWriter; import java.util.HashMap; import java.util.Map; /** * @ProjectName: springbootSecurityJwtOAuth * @Package: cn.**.filter * @Author: huat * @Date: 2019/12/26 13:18 * @Version: 1.0 */ @Component public class LoginInterceptor implements HandlerInterceptor { @Autowired private UserDao userDao; @Autowired private RsaKeyProperties rsaKeyProperties; //這個方法是在訪問接口以前執行的,咱們只須要在這裏寫驗證登錄狀態的業務邏輯,就能夠在用戶調用指定接口以前驗證登錄狀態了 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //獲取請求頭中的信息 String header=request.getHeader("Authorization"); if (null!=header&&header.startsWith("bearer ")){ //獲取token String requestToken=header.replace("bearer ",""); //驗證token是否正確 Payload<AuthorityUser> payload= JwtUtil.getInfoFromToken(requestToken,rsaKeyProperties.getPublicKey(), AuthorityUser.class); AuthorityUser user=payload.getUserInfo(); String token=userDao.getTokenById(user.getId()); if(null!=token&&!"".equals(token)){ if( token.equals(requestToken)){ return true; } //若是沒有登錄提示用戶進行登錄 response.setContentType("application/json;charset=utf-8"); //返回403狀態碼 response.setStatus(HttpServletResponse.SC_FORBIDDEN); //響應流 PrintWriter out = response.getWriter(); //封裝返回數據 Map<String, Object> resultMap = new HashMap<String, Object>(); resultMap.put("code", HttpServletResponse.SC_FORBIDDEN); resultMap.put("data", "您的帳號在其餘地方已登陸"); resultMap.put("msg", "請登陸"); //new ObjectMapper().writeValueAsString(resultMap)將map轉成json out.write(new ObjectMapper().writeValueAsString(resultMap)); out.flush(); out.close(); return false; } } //若是沒有登錄提示用戶進行登錄 response.setContentType("application/json;charset=utf-8"); //返回403狀態碼 response.setStatus(HttpServletResponse.SC_FORBIDDEN); //響應流 PrintWriter out = response.getWriter(); //封裝返回數據 Map<String, Object> resultMap = new HashMap<String, Object>(); resultMap.put("code", HttpServletResponse.SC_FORBIDDEN); resultMap.put("data", ""); resultMap.put("msg", "請登陸"); //new ObjectMapper().writeValueAsString(resultMap)將map轉成json out.write(new ObjectMapper().writeValueAsString(resultMap)); out.flush(); out.close(); return false; } }
package cn.**.filter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.util.ResourceUtils; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * @Author: huat * @Date: 2019/7/26 10:18 * @Version: 1.0 */ @Configuration public class WebConfigurer implements WebMvcConfigurer { @Autowired private LoginInterceptor loginInterceptor; // 這個方法是用來配置靜態資源的,好比html,js,css,等等 @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { //super.addResourceHandlers(registry); } // 這個方法用來註冊攔截器,咱們本身寫好的攔截器須要經過這裏添加註冊才能生效 @Override public void addInterceptors(InterceptorRegistry registry) { // addPathPatterns("/**") 表示攔截全部的請求, //excludePathPatterns("/login", "/register"); 表示除了登錄與註冊以外,由於登錄註冊不須要登錄也能夠訪問 registry.addInterceptor(loginInterceptor).addPathPatterns("/**").excludePathPatterns("/loginInto", "/login","/login/**","/css/**","/font/**","/img/**","/images/**","/js/**","/lay/**"); //super.addInterceptors(registry); //較新Spring Boot的版本中這裏能夠直接去掉,不然會報錯 } }