如何在 Spring 安全性中刷新令牌 [英] How can I refresh tokens in Spring security

查看:32
本文介绍了如何在 Spring 安全性中刷新令牌的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这一行:

Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();

当我的 jwt 令牌过期时抛出这样的错误:

Throws an error like this when my jwt token expires:

JWT 于 2020-05-13T07:50:39Z 过期.当前时间:2020-05-16T21:29:41Z.

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

更具体地说,正是这个函数抛出了ExpiredJwtException".例外 :

More specifically, it is this function that throws the "ExpiredJwtException" exception :

我该如何处理这些异常?我是否应该捕获它们并将错误消息发送回客户端并强制它们重新登录?

How do I go about handling these exceptions? Should I catch them and send back to the client an error message and force them to re-login?

如何实现刷新令牌功能?我在后端使用 Spring 和 mysql,在前端使用 vuejs.

How can I implement a refresh tokens feature? I'm using Spring and mysql in the backend and vuejs in the front end.

我像这样生成初始令牌:

I generate the initial token like this:

   @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 类中:

And then in the JwtUtil class:

   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 函数,用于过滤每个请求:

For more info, here is my doFilterInternal function that filters every request:

   @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);
        }
    } 

推荐答案

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

There are 2 main approaches to deal with such situations:

在这种情况下,流程如下:

In this case, the flow is the following one:

  1. 用户登录应用程序(包括用户名密码)

您的后端应用程序返回任何所需的凭据信息,并且:

Your backend application returns any required credentials information and:

2.1 访问 JWT 令牌,过期时间通常低";(15、30 分钟等).

2.1 Access JWT token with an expired time usually "low" (15, 30 minutes, etc).

2.2 刷新 JWT 令牌,过期时间大于访问时间.

2.2 Refresh JWT token with an expired time greater than access one.

从现在开始,您的前端应用程序将在每个请求的 Authorization 标头中使用 access token.

From now, your frontend application will use access token in the Authorization header for every request.

当后端返回 401 时,前端应用程序将尝试使用 refresh token(使用特定端点)来获取新凭据,而不强制用户再次登录.

When backend returns 401, the frontend application will try to use refresh token (using an specific endpoint) to get new credentials, without forcing the user to login again.

刷新代币流(这只是一个例子,通常只发送刷新令牌)

如果没有问题,那么用户将能够继续使用该应用程序.如果后端返回一个新的 401 =>前端应该重定向到登录页面.

If there is no problem, then the user will be able to continue using the application. If backend returns a new 401 => frontend should redirect to login page.

在这种情况下,流程与上一个类似,您可以创建自己的端点来处理这种情况:/auth/token/extend(例如),包括过期的 Jwt作为请求的参数.

In this case, the flow is similar to the previous one and you can create your own endpoint to deal with such situations: /auth/token/extend (for example), including the expired Jwt as parameter of the request.

现在由您来管理:

  • 过期的 Jwt 令牌有效"多长时间?扩展它?

新端点将具有与上一节中的刷新类似的行为,我的意思是,将返回一个新的 Jwt 令牌或 401 因此,从前端的角度来看,流程将是一样.

The new endpoint will have a similar behaviour of refresh one in the previous section, I mean, will return a new Jwt token or 401 so, from the point of view of frontend the flow will be the same.

一个重要的事情,独立于您想要遵循的方法,新端点"应该从所需的 Spring 身份验证端点中排除,因为您将自己管理安全性:

One important thing, independently of the approach you want to follow, the "new endpoint" should be excluded from the required Spring authenticated endpoints, because you will manage the security by yourself:

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 安全性中刷新令牌的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆