spring security 使用JWT进行权限认证
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 {
//忽略请求,不经过security过滤器链
web.ignoring().mvcMatchers(HttpMethod.GET,"/**");
}
/**
* 从容器中取出 AuthenticationManagerBuilder,执行方法里面的逻辑之后,放回容器
* @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 预检请求放行,让Spring security 放行所有preflight request(cors 预检请求)
http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
//让Security永远不会创建HttpSession,它不会使用HttpSession来获取SecurityContext
http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().headers().cacheControl();
http.authorizeRequests()
.antMatchers("/admin/**")
.hasAuthority("root")
.antMatchers("/").permitAll();
//token权限解析认证
http.addFilterBefore(authJwtFilter,UsernamePasswordAuthenticationFilter.class);
//登陆
http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
//处理异常情况:认证失败和权限不足
http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
response.setContentType("application/json;charset=utf8");
Object error = request.getAttribute("error");
if (ObjectUtils.isEmpty(error)){
response.getWriter().println("权限不足");
return;
}
response.getWriter().println(error);
}).accessDeniedHandler((request, response, accessDeniedException) -> {
response.setContentType("application/json;charset=utf8");
response.getWriter().println("你的权限不足以访问该资源");
});
}
/**
* 登陆拦截器
* @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 认证解析拦截器
**/
@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,方便后续校验
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(
authUser.getUsername(),
null,
authUser.getAuthorities()
);
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
// 将 authentication 存入 ThreadLocal,方便后续获取用户信息
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
filterChain.doFilter(request, response);
}
}
JWT工具类
/**
* JWT生成令牌、验证令牌、获取令牌
*/
@Component
@NoArgsConstructor
public class JwtTokenUtil {
/**
* 私钥
*/
private static final String SECRET_KEY = "auth_sp";
/**
* 过期时间 毫秒,设置默认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));
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @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();
}
/**
* 从令牌中获取数据声明
*
* @param token 令牌
* @return 数据声明
*/
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破坏