Spring MVC 上的 JWT 注销
Posted
技术标签:
【中文标题】Spring MVC 上的 JWT 注销【英文标题】:JWT logout on Spring MVC 【发布时间】:2018-09-23 20:02:34 【问题描述】:我有一个小问题要解决,我写了一个用于生成和验证 JWT 令牌的类,但我走到了一个十字路口:如何使令牌无效以实现注销功能?使用 Spring MVC 创建简单的 API rest 以与 Angular 5 中的外部前端进行交互。
代码:
import java.security.Key;
import java.util.Date;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Clock;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.DefaultClock;
@SuppressWarnings("restriction")
public class SecurityUtil
private static final String secretKey = "secret";
private static final Logger logger = LoggerFactory.getLogger(SecurityUtil.class);
private static Clock clock = DefaultClock.INSTANCE;
public static PasswordEncoder passwordEncoder()
return new BCryptPasswordEncoder();
private static Date getExpirationDateFromToken(String token)
Claims claims = getAllClaimsFromToken(token);
if(claims != null)
return claims.getExpiration();
return null;
private static Claims getAllClaimsFromToken(String token)
try
return Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(secretKey))
.parseClaimsJws(token)
.getBody();
catch(Exception ex)
logger.info(ex.getMessage());
return null;
private static Boolean isTokenExpired(String token)
Date expiration = getExpirationDateFromToken(token);
if(expiration != null)
return expiration.before(clock.now());
return null;
private static Date calculateExpirationDate(Date createdDate)
return new Date(createdDate.getTime() + (60 * 15) * 1000);
public static Boolean validateToken(String token)
try
return !isTokenExpired(token);
catch(Exception ex)
return false;
public static String generateToken(String id, String subject)
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
Date createdDate = clock.now();
Date expirationDate = calculateExpirationDate(createdDate);
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(secretKey);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
return Jwts.builder()
.setId(id)
.setIssuedAt(createdDate)
.setSubject(subject)
.signWith(signatureAlgorithm, signingKey)
.setExpiration(expirationDate)
.compact();
public static String refreshToken(String token)
Date createdDate = clock.now();
Date expirationDate = calculateExpirationDate(createdDate);
final Claims claims = getAllClaimsFromToken(token);
claims.setIssuedAt(createdDate);
claims.setExpiration(expirationDate);
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, DatatypeConverter.parseBase64Binary(secretKey))
.compact();
我还注意到,在我刷新令牌后,过去的令牌并没有失效。
有人可以帮助我吗?
提前致谢
【问题讨论】:
【参考方案1】:您对 JWT 的选择确实有限,因为它是一个无状态令牌,应该保存在客户端而不是服务器上,因此不能轻易失效。当然,您可以为服务器上的无效令牌添加自定义黑名单,但此解决方案无法很好地扩展,因为您需要将黑名单分发到所有应用程序实例(您可以使用任何分布式缓存解决方案用于黑名单存储)。
也就是说,如果你将它与令牌本身的短 TTL 结合起来,它可能仍然可以正常工作,因为你可以对黑名单实施非常积极的清理策略(因为保留条目是没有意义的黑名单的时间长于令牌 TTL),从而减少了您需要复制的数据量。
【讨论】:
【参考方案2】:我遇到了同样的问题,但我无法解决。我将到期日期更改为 2 分钟
【讨论】:
以上是关于Spring MVC 上的 JWT 注销的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring Boot 后端使用 jwt 令牌实现注销功能(使用休息端点)
如何在 Spring Security 无状态(jwt 令牌)中注销用户?
Spring Boot JWT Authentication:登录和注销后触发一个方法