java JWT 认证:如何实现注销?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/43569723/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 07:33:29  来源:igfitidea点击:

JWT authentication: How to implement logout?

javaspring

提问by Arian

I implemented JWT authentication for my Spring boot app. Overally, it works like this:

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

  1. Client sends username, password to the login endpoint.
  2. Server checks if the provided credentials are valid.
  3. If no, it would return an error
  4. If yes, it would return a token, that token actually includes
  5. Client sends that token with every future request
  1. 客户端将用户名、密码发送到登录端点。
  2. 服务器检查提供的凭据是否有效。
  3. 如果不是,它会返回一个错误
  4. 如果是,它将返回一个令牌,该令牌实际上包括
  5. 客户端在以后的每个请求中发送该令牌

The question is, how should we implement logout ?

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

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

addAuthenticationis used by the JWTLoginFilterclass to send the authentication code when logging in, 'getAuthenticationis used by theJWTAuthenticationFilter` that filter all requests to the end points.

addAuthentication所使用的JWTLoginFilter类登录时发送的验证码,“getAuthentication is used by theJWTAuthenticationFilter`该过滤器到终点的所有请求。

What is the best practice here ?

这里的最佳做法是什么?

回答by Indrek Ots

I don't think there is a best practice here. I guess it depends on the application you're building and it's requirements.

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

The benefit of JWT is that they're stateless. You don't need to query the database to validate the token. This is good when you wish to reduce the load on your database but bad when you want to invalidate an existing non-expired token.

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

Possible solutions:

可能的解决方案:

  • Store JWT in the database. You can check which tokens are valid and which ones are revoked but this defeats the purpose of using JWT at all in my opinion.
  • Delete token from the client. This would stop the client from being able to make authenticated requests but if the token is still valid and somebody else has access to it, the token could still be used. This leads me to my next point.
  • Short token lifetime. Let the tokens expire quickly. Depending on the application, it could be several minutes or half an hour. When the client deletes its token, there's a short window of time where it can still be used. Deleting the token from the client and having short token lifetimes would not require major modifications on the back-end. But short token lifetimes would mean that the user is constantly being logged out because the token has expired.
  • Rotate tokens. Maybe introduce a concept of refresh tokens. When the user logs in, provide them with a JWT and a refresh token. Store the refresh token in a database. For authenticated requests, the client can use the JWT but when the token expires (or is about to expire), let the client make a request with the refresh token in exchange for a new JWT. This way you would only have to hit the database when a user logs in or asks for a new JWT. When the user logs out, you would need to invalidate the stored refresh token. Otherwise somebody listening in on the connection could still get new JWTs even though the user had logged out.
  • Create a JWT blacklist. Depending on the expiration time, when the client deletes its token, it might still be valid for some time. If the token lifetime is short, it might not be an issue, but if you still wish that the token is invalidated immediately, you could create a token blacklist. When the back-end receives a logout request, take the JWT from the request and store it in an in-memory database. For each authenticated request you would need to check your in-memory database to see if the token has been invalidated. To keep the search space small, you could remove tokens from the blacklist which have already expired.
  • 将 JWT 存储在数据库中。您可以检查哪些令牌是有效的,哪些是被撤销的,但在我看来,这完全违背了使用 JWT 的目的。
  • 从客户端删除令牌。这将阻止客户端发出经过身份验证的请求,但如果令牌仍然有效并且其他人可以访问它,则仍然可以使用令牌。这将我引向我的下一点。
  • 令牌寿命短。让令牌快速过期。根据应用程序的不同,可能需要几分钟或半小时。当客户端删除其令牌时,它仍然可以使用的时间很短。从客户端删除令牌并具有较短的令牌生命周期不需要对后端进行重大修改。但是令牌生命周期短意味着用户会因为令牌已过期而不断被注销。
  • 轮换令牌。也许引入刷新令牌的概念。当用户登录时,为他们提供 JWT 和刷新令牌。将刷新令牌存储在数据库中。对于经过身份验证的请求,客户端可以使用 JWT,但是当令牌过期(或即将过期)时,让客户端使用刷新令牌发出请求以换取新的 JWT。这样,您只需在用户登录或请求新的 JWT 时访问数据库。当用户注销时,您需要使存储的刷新令牌无效。否则,即使用户已注销,监听连接的人仍然可以获得新的 JWT。
  • 创建 JWT 黑名单。根据到期时间,当客户端删除其令牌时,它可能仍然有效一段时间。如果令牌生命周期很短,这可能不是问题,但如果您仍然希望令牌立即失效,则可以创建令牌黑名单。当后端收到注销请求时,从请求中取出 JWT 并将其存储在内存数据库中。对于每个经过身份验证的请求,您都需要检查内存数据库以查看令牌是否已失效。为了保持搜索空间较小,您可以从黑名单中删除已经过期的令牌。

回答by Mike Nakis

I don't know what's best practice, but in a system whose internals I have seen, there is a central authentication manager which knows all the currently valid authentication tokens, so logging out would simply consist of removing the token from the collection of valid tokens.

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

So, next time the authentication manager is asked whether the token is valid, it would respond with a "no".

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

回答by Ziaullhaq Savanur

1) Simply remove the token from the client

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

2) Create a token blacklist

2)创建token黑名单

3) Just keep token expiry times short and rotate them often

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

Please have a look at Invalidating JSON Web Tokens Invalidating JSON Web Tokens

请查看 Invalidating JSON Web Tokens Invalidating JSON Web Tokens