java - JWT认证: How to implement logout?

标签 java spring

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

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

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

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

addAuthenticationJWTLoginFilter 类用来在登录时发送验证码,'getAuthenticationJWTAuthenticationFilter 用来过滤所有对端点的请求。

此处的最佳做法是什么?

最佳答案

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

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

可能的解决方案:

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

关于java - JWT认证: How to implement logout?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43569723/

相关文章:

java - 字符串池——字符串总是存在于常量池中吗?

java - 在现有应用程序中实现缓存

java - Spring 依赖注入(inject)和插件 Jar

java - 如何在 Java 8 中将 LocalDateTime 转换为 Date

java - 属性文件到复杂的 JSON 字符串 [Java/Spring]

java - 如何连接OpenJPA、Spring和PostgreSQL?

java - Spring Data 自定义实现 : Entity Not Managed

java - 常见 spring NoRepositoryBean 基接口(interface)上的 PreAuthorize 问题

java - Jooq Global Artefacts 重命名

java - 如何在Java 8中检查文件是否可以成功创建