繁体   English   中英

JWT 认证:如何实现注销?

[英]JWT authentication: How to implement logout?

我为 Spring boot 应用程序实现了 JWT 身份验证。 总的来说,它是这样工作的:

  1. 客户端将用户名、密码发送到登录端点。
  2. 服务器检查提供的凭据是否有效。
  3. 如果不是,它会返回一个错误
  4. 如果是,它将返回一个令牌,该令牌实际上包括
  5. 客户端在以后的每个请求中发送该令牌

问题是,我们应该如何实现注销?

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

addAuthentication类使用JWTLoginFilter来在登录时发送身份验证代码,JWTAuthenticationFilter is used by the “getAuthentication is used by the来过滤对端点的所有请求。

这里的最佳做法是什么?

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

JWT 的好处是它们是无状态的。 您不需要查询数据库来验证令牌。 当您希望减少数据库的负载时,这很好,但当您想让现有的未过期令牌无效时,这很糟糕。

可能的解决方案:

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

我不知道什么是最佳实践,但在我见过内部结构的系统中,有一个中央身份验证管理器知道所有当前有效的身份验证令牌,因此注销只需从有效令牌集合中删除令牌.

因此,下次向身份验证管理器询问令牌是否有效时,它将以“否”作为响应。

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

2)创建令牌黑名单

3)保持令牌到期时间短并经常轮换它们

请查看 Invalidating JSON Web Tokens Invalidating JSON Web Tokens

如果令牌保存在 localStorage 中:

    localStorage.removeItem('token');

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

     req.session = null;

如果你想清除你设置的cookie

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM