JWT身份验证:如何实现注销?

时间:2017-04-23 10:03:38

标签: java spring

我为Spring启动应用程序实现了JWT身份验证。总的来说,它的工作原理如下:

  1. 客户端将用户名,密码发送到登录端点。
  2. 服务器检查提供的凭据是否有效。
  3. 如果不是,则会返回错误
  4. 如果是,它将返回一个令牌,该令牌实际上包括
  5. 客户端会在每个将来的请求中发送该令牌
  6. 问题是,我们应该如何实施注销?

    import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.core.userdetails.UserDetailsService;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import java.util.Date;
    
    class TokenAuthenticationService {
        static final long EXPIRATIONTIME = 864_000_000; // 10 days
        static final String SECRET = "ThisIsASecret";
        static final String TOKEN_PREFIX = "Bearer";
        static final String HEADER_STRING = "Authorization";
    
        static void addAuthentication(HttpServletResponse res, String username) {
            String JWT = Jwts
                    .builder()
                    .setSubject(username)
                    .setExpiration(
                            new Date(System.currentTimeMillis() + EXPIRATIONTIME))
                    .signWith(SignatureAlgorithm.HS512, SECRET).compact();
            res.addHeader(HEADER_STRING, TOKEN_PREFIX + " " + JWT);
        }
    
        static Authentication getAuthentication(HttpServletRequest request, UserDetailsService customUserDetailsService) {
            String token = request.getHeader(HEADER_STRING);
            if (token != null) {
                // parse the token.
                Claims claims = Jwts.parser().setSigningKey(SECRET)
                        .parseClaimsJws(token.replace(TOKEN_PREFIX, "")).getBody();
                String userName = claims.getSubject();
                Date expirationTime = claims.getExpiration();
                if (expirationTime.compareTo(new Date()) < 0) {
                    return null;
                }
                UserDetails user = customUserDetailsService.loadUserByUsername(userName);
                return user != null ? new UsernamePasswordAuthenticationToken(user.getUsername(),
                        user.getPassword(), user.getAuthorities()) : null;
            }
            return null;
        }
    }
    
    {p> addAuthenticationJWTLoginFilter类用于在登录时发送身份验证代码,&#39; getAuthentication is used by the JWTAuthenticationFilter`,用于过滤对端点的所有请求。

    这里的最佳做法是什么?

4 个答案:

答案 0 :(得分:16)

我认为这里没有最好的做法。我想这取决于您正在构建的应用程序及其要求。

JWT的好处是他们无国籍。您不需要查询数据库来验证令牌。当您希望减少数据库的负载时这很好,但是当您想要使现有的非过期令牌无效时,这是很好的。

可能的解决方案:

  • 将JWT存储在数据库中。您可以检查哪些令牌有效以及哪些令牌被撤销,但这在我看来完全违背了使用JWT的目的。
  • 从客户端删除令牌。这将阻止客户端进行经过身份验证的请求,但如果令牌仍然有效且其他人可以访问它,则仍可以使用该令牌。这让我想到了下一点。
  • 令牌生命周期短。让令牌快速到期。根据应用,可能是几分钟或半小时。当客户端删除其令牌时,可以使用它的短时间窗口。从客户端删除令牌并具有短令牌生存期不需要对后端进行重大修改。但是,令牌生命周期短意味着用户因令牌已过期而不断被注销。
  • 旋转代币。也许引入刷新令牌的概念。当用户登录时,为他们提供JWT和刷新令牌。将刷新令牌存储在数据库中。对于经过身份验证的请求,客户端可以使用JWT,但是当令牌过期(或即将过期)时,让客户端使用刷新令牌发出请求以换取新的JWT。这样,您只需在用户登录或要求新的JWT时访问数据库。当用户注销时,您需要使存储的刷新令牌无效。否则,即使用户已经注销,有人仍然可以获得新的JWT。
  • 创建JWT黑名单。根据过期时间,当客户端删除其令牌时,它可能仍然有效一段时间。如果令牌生存期很短,则可能不是问题,但如果您仍希望令牌立即失效,则可以创建令牌黑名单。当后端收到注销请求时,从请求中获取JWT并将其存储在内存数据库中。对于每个经过身份验证的请求,您需要检查内存数据库以查看令牌是否已失效。为了保持较小的搜索空间,您可以从黑名单中删除已经过期的令牌。

答案 1 :(得分:3)

我不知道什么是最佳做法,但在我见过的内部系统中,有一个中央身份验证管理器,它知道所有当前有效的身份验证令牌,因此注销只包含从有效令牌集合中删除令牌。

因此,下次向身份验证管理器询问令牌是否有效时,它会响应&#34; no&#34;。

答案 2 :(得分:0)

1)只需从客户端删除令牌

2)创建一个令牌黑名单

3)只需缩短令牌到期时间并经常轮换它们

请查看使JSON Web令牌无效 Invalidating JSON Web Tokens

答案 3 :(得分:0)

如果令牌保存在 localStorage 中:

    localStorage.removeItem('token');

在服务器中,如果 jwt 在会话中:

     req.session = null;

如果你想清除你设置的cookie

  req.session.destroy((err) => { //express-session
    res.clearCookie("qid"); //qid is the cookie name
  }