spring security 使用JWT進(jìn)行權(quán)限認(rèn)證
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
?
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
</dependency>
?
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
<scope>compile</scope>
</dependency>
?
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency></pre>
配置類
/**
* @author spp
* @date 2020-06-11 14:17
**/
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
//忽略請求,不經(jīng)過security過濾器鏈
web.ignoring().mvcMatchers(HttpMethod.GET,"/**");
}
?
/**
* 從容器中取出 AuthenticationManagerBuilder,執(zhí)行方法里面的邏輯之后,放回容器
* @param authenticationManagerBuilder x
* @throws Exception
*/
@Autowired
public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
authenticationManagerBuilder.userDetailsService(userDetails).passwordEncoder(new BCryptPasswordEncoder());
}
?
@Override
public void configure(HttpSecurity http) throws Exception {
//解決跨域問題。cors 預(yù)檢請求放行,讓Spring security 放行所有preflight request(cors 預(yù)檢請求)
http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
//讓Security永遠(yuǎn)不會創(chuàng)建HttpSession宇葱,它不會使用HttpSession來獲取SecurityContext
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().headers().cacheControl();
?
http.authorizeRequests()
.antMatchers("/admin/**")
.hasAuthority("root")
.antMatchers("/").permitAll();
?
//token權(quán)限解析認(rèn)證
http.addFilterBefore(authJwtFilter,UsernamePasswordAuthenticationFilter.class);
?
//登陸
http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
//處理異常情況:認(rèn)證失敗和權(quán)限不足
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.setContentType("application/json;charset=utf8");
Object error = request.getAttribute("error");
if (ObjectUtils.isEmpty(error)){
response.getWriter().println("權(quán)限不足");
return;
}
response.getWriter().println(error);
}).accessDeniedHandler((request, response, accessDeniedException) -> {
response.setContentType("application/json;charset=utf8");
response.getWriter().println("你的權(quán)限不足以訪問該資源");
});
}
?
/**
* 登陸攔截器
* @return
* @throws Exception
*/
@Bean
public UsernamePassAuthFilter myUsernamePasswordAuthenticationFilter() throws Exception {
UsernamePassAuthFilter filter = new UsernamePassAuthFilter();
//成功后處理
filter.setAuthenticationSuccessHandler(authSuccessHandler);
//失敗后處理
filter.setAuthenticationFailureHandler(authFailHandler);
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
?
}
最重要的http.addFilterBefore(authJwtFilter,UsernamePasswordAuthenticationFilter.class);
JWT過濾器
/**
* @author spp
* @date 2020-06-11 16:21
* jwt token 認(rèn)證解析攔截器
**/
@Component
public class AuthJwtFilter extends OncePerRequestFilter {
public final String HEADER = "Authorization";
?
/**
* @param request rq
* @param response rs
* @param filterChain 鏈
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
String token = request.getHeader(HEADER);
if (!ObjectUtils.isEmpty(token)){
logger.info("token-->"+token);
//是否過期
boolean check = false;
try {
check = JwtTokenUtil.isTokenExpired(token);
} catch (Exception e) {
request.setAttribute("error",e.getMessage());
}
if (!check){
String username = JwtTokenUtil.getUsernameFromToken(token);
if (username != null){
//通過用戶信息得到UserDetails
List<SimpleGrantedAuthority> list = new ArrayList<>();
list.add(new SimpleGrantedAuthority("root"));
AuthUser authUser = new AuthUser(username,null,list);
//將用戶信息存入 authentication,方便后續(xù)校驗
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
authUser.getUsername(),
null,
authUser.getAuthorities()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 將 authentication 存入 ThreadLocal,方便后續(xù)獲取用戶信息
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
?
}
filterChain.doFilter(request, response);
}
}
JWT工具類
/**
* JWT生成令牌唯卖、驗證令牌、獲取令牌
*/
@Component
@NoArgsConstructor
public class JwtTokenUtil {
/**
* 私鑰
*/
?
private static final String SECRET_KEY = "auth_sp";
?
/**
* 過期時間 毫秒,設(shè)置默認(rèn)1周的時間過期
*/
private static final long EXPIRATION_TIME = 3600000L * 2;
?
/**
* 生成令牌
* @param userDetails 用戶
* @return 令牌
*/
public static String generateToken(UserDetails userDetails) {
Map<String, Object> claims = new HashMap<>(2);
claims.put(Claims.SUBJECT, userDetails.getUsername());
claims.put(Claims.ISSUED_AT, new Date());
return generateToken(claims);
}
?
/**
* 從令牌中獲取用戶名
* @param token 令牌
* @return 用戶名
*/
public static String getUsernameFromToken(String token) {
String username = null;
try {
Claims claims = getClaimsFromToken(token);
username = claims.getSubject();
} catch (Exception e) {
System.out.println("e = " + e.getMessage());
}
return username;
}
?
/**
* 判斷令牌是否過期
*
* @param token 令牌
* @return 是否過期
*/
public static Boolean isTokenExpired(String token) throws Exception{
try {
Claims claims = getClaimsFromToken(token);
Date expiration = claims.getExpiration();
return expiration.before(new Date());
} catch (Exception e) {
throw new Exception("簽名過期");
}
}
?
/**
* 刷新令牌
*
* @param token 原令牌
* @return 新令牌
*/
public static String refreshToken(String token) {
String refreshedToken;
try {
Claims claims = getClaimsFromToken(token);
claims.put(Claims.ISSUED_AT, new Date());
refreshedToken = generateToken(claims);
} catch (Exception e) {
refreshedToken = null;
}
return refreshedToken;
}
?
/**
* 驗證令牌
*
* @param token 令牌
* @param userDetails 用戶
* @return 是否有效
*/
public static Boolean validateToken(String token, UserDetails userDetails) throws Exception {
AuthUser user = (AuthUser) userDetails;
String username = getUsernameFromToken(token);
return (username.equals(user.getUsername()) && !isTokenExpired(token));
}
?
/**
* 從數(shù)據(jù)聲明生成令牌
*
* @param claims 數(shù)據(jù)聲明
* @return 令牌
*/
private static String generateToken(Map<String, Object> claims) {
Date expirationDate = new Date(System.currentTimeMillis()+ EXPIRATION_TIME);
HashMap<String, Object> map = new HashMap<>(1);map.put("typ",Header.JWT_TYPE);
return Jwts.builder().setHeader(map).setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, SECRET_KEY).compact();
}
?
/**
* 從令牌中獲取數(shù)據(jù)聲明
*
* @param token 令牌
* @return 數(shù)據(jù)聲明
*/
private static Claims getClaimsFromToken(String token) throws Exception {
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
} catch (Exception e) {
new Throwable(e);
}
return claims;
}
}
測試
不攜帶token
攜帶正確的token
故意將token破壞