spring - 如何在 Spring 安全性中刷新 token

标签 spring spring-security jwt access-token refresh-token

这一行:

Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
当我的 jwt token 过期时抛出这样的错误:

JWT expired at 2020-05-13T07:50:39Z. Current time: 2020-05-16T21:29:41Z.


更具体地说,正是这个函数抛出了“ExpiredJwtException”异常:
parseClaimsJws() function
我该如何处理这些异常?我是否应该捕获它们并将错误消息发送回客户端并强制它们重新登录?
如何实现刷新 token 功能?我在后端使用 Spring 和 mysql,在前端使用 vuejs。
我生成这样的初始 token :
   @Override
        public JSONObject login(AuthenticationRequest authreq) {
            JSONObject json = new JSONObject();
    
            try {
                Authentication authentication = authenticationManager.authenticate(
                        new UsernamePasswordAuthenticationToken(authreq.getUsername(), authreq.getPassword()));
    
                UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
                List<String> roles = userDetails.getAuthorities().stream().map(item -> item.getAuthority())
                        .collect(Collectors.toList());
    
                if (userDetails != null) {
    
                    final String jwt = jwtTokenUtil.generateToken(userDetails);
    
    
                    JwtResponse jwtres = new JwtResponse(jwt, userDetails.getId(), userDetails.getUsername(),
                            userDetails.getEmail(), roles, jwtTokenUtil.extractExpiration(jwt).toString());
    
                    return json.put("jwtresponse", jwtres);
                }
            } catch (BadCredentialsException ex) {
                json.put("status", "badcredentials");
            } catch (LockedException ex) {
                json.put("status", "LockedException");
            } catch (DisabledException ex) {
                json.put("status", "DisabledException");
            }
    
            return json;
        }
然后在 JwtUtil 类中:
   public String generateToken(UserDetails userDetails) {
            Map<String, Object> claims = new HashMap<>();
            return createToken(claims, userDetails.getUsername());
        }
    
   private String createToken(Map<String, Object> claims, String subject) {
            return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                    .setExpiration(new Date(System.currentTimeMillis() + EXPIRESIN))
                    .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
        }
有关更多信息,这是我的 doFilterInternal 函数,用于过滤每个请求:
   @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException, ExpiredJwtException, MalformedJwtException {

        try {

            final String authorizationHeader = request.getHeader("Authorization");

            String username = null;
            String jwt = null;

            if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
                jwt = authorizationHeader.substring(7);
                username = jwtUtil.extractUsername(jwt);
            }

            if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
                UserDetails userDetails = userService.loadUserByUsername(username);

                boolean correct = jwtUtil.validateToken(jwt, userDetails);

                if (correct) {
                    UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                            userDetails, null, userDetails.getAuthorities());

                    usernamePasswordAuthenticationToken
                            .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);

                }
            }

            chain.doFilter(request, response);
        } catch (ExpiredJwtException ex) {
            resolver.resolveException(request, response, null, ex);
        }
    } 

最佳答案

有两种主要的方法来处理这种情况:

管理访问和刷新 token
在这种情况下,流程如下:

  • 用户登录应用程序(包括 usernamepassword )
  • 您的后端应用程序返回任何必需的凭据信息,并且:
    2.1 访问 JWT token 过期时间通常“低”(15、30 分钟等)。
    2.2 刷新 JWT token 过期时间大于访问时间。
  • 从现在开始,您的前端应用程序将使用 access tokenAuthorization每个请求的 header 。

  • 当后端返回时 401 ,前端应用会尝试使用refresh token (使用特定端点)获取新凭据,而不强制用户再次登录。
    Refresh token flow
    (这只是一个例子,通常只发送刷新 token )
    如果没有问题,那么用户将能够继续使用该应用程序。如果后端返回一个新的 401 => 前端应该重定向到登录页面。

    仅管理一个 Jwt token
    在这种情况下,流程与上一个类似,您可以创建自己的端点来处理这种情况:/auth/token/extend (例如),包括过期的 Jwt 作为请求的参数。
    现在由您来管理:
  • 过期的 Jwt token 将在多长时间内“有效”以延长它?

  • 新端点将具有与上一节中的 refresh one 类似的行为,我的意思是,将返回一个新的 Jwt token 或 401所以,从前端的角度来看,流程是一样的。

    重要的事情 ,独立于您要遵循的方法,“新端点”应从所需的 Spring 身份验证端点中排除,因为您将自己管理安全性:
    public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
      ..
    
      @Override
      protected void configure(HttpSecurity http) throws Exception {
        http.
          ..
          .authorizeRequests()
          // List of services do not require authentication
          .antMatchers(Rest Operator, "MyEndpointToRefreshOrExtendToken").permitAll()
          // Any other request must be authenticated
          .anyRequest().authenticated()
          ..
       }
    }
    

    关于spring - 如何在 Spring 安全性中刷新 token ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63426347/

    相关文章:

    Spring Security 使用 X.509 身份验证并定期更新授权用户详细信息

    java - Spring MVC 文件下载 IOUtils.copy 有效,但 FileCopyUtils 无效

    java - 使用 Spring、JPA 和 Hibernate 访问在 Jboss 中配置的多个数据库/数据源

    spring-mvc - 在 spring 框架 3.2.4 应用程序上使用 OWASP_CSRFTOKEN 的 session.getAttribute() 键为空

    oauth-2.0 - 如何使用 Google Auth JWT 授权登录用户? (图片)

    java - 我的方法安全吗?

    java - 在 Spring MVC 中处理 JWT 异常

    java - 使用 apache HTTPS 库时无法在 Java 中创建客户端

    mysql - Spring MVC 中 Controller 操作之间的文件数据传播

    java - 当用户未经身份验证时,Spring Security 不会重定向到登录页面