美文网首页
spring security 使用JWT进行权限认证

spring security 使用JWT进行权限认证

作者: Sunshine__7b8f | 来源:发表于2020-06-11 17:28 被阅读0次

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破坏

相关文章

网友评论

      本文标题:spring security 使用JWT进行权限认证

      本文链接:https://www.haomeiwen.com/subject/kytitktx.html