JWT架构:Token结构与刷新安全
JWT架构:Token结构与刷新安全
JWT令牌结构
JWT由Header、Payload和Signature三部分组成,每部分使用Base64URL编码。
// JWT令牌构建
@Component
public class JWTTokenBuilder {
private final RSAPrivateKey privateKey;
private final JWTConfig config;
public String createAccessToken(User user, List<String> scopes) {
Instant now = Instant.now();
String token = Jwts.builder()
.setSubject(user.getId())
.setIssuer(config.getIssuer())
.setAudience(config.getAudience())
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(now.plus(config.getAccessTokenLifetime())))
.claim("email", user.getEmail())
.claim("roles", user.getRoles())
.claim("scope", String.join(" ", scopes))
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
return token;
}
public RefreshToken createRefreshToken(User user) {
Instant now = Instant.now();
String tokenId = UUID.randomUUID().toString();
String token = Jwts.builder()
.setId(tokenId)
.setSubject(user.getId())
.setIssuer(config.getIssuer())
.setIssuedAt(Date.from(now))
.setExpiration(Date.from(now.plus(config.getRefreshTokenLifetime())))
.claim("token_type", "refresh")
.signWith(privateKey, SignatureAlgorithm.RS256)
.compact();
return RefreshToken.builder()
.token(token)
.tokenId(tokenId)
.userId(user.getId())
.expiresAt(now.plus(config.getRefreshTokenLifetime()))
.build();
}
}
令牌验证与解析
// JWT验证服务
@Service
public class JWTValidator {
private final RSAPublicKey publicKey;
private final JWTConfig config;
private final TokenBlacklistService blacklistService;
public Claims validateToken(String token) {
try {
// 解析并验证签名
Claims claims = Jwts.parserBuilder()
.setSigningKey(publicKey)
.requireIssuer(config.getIssuer())
.requireAudience(config.getAudience())
.build()
.parseClaimsJws(token)
.getBody();
// 检查黑名单
if (blacklistService.isBlacklisted(claims.getId())) {
throw new JWTException("令牌已被撤销");
}
// 验证过期时间
if (claims.getExpiration().before(new Date())) {
throw new JWTException("令牌已过期");
}
return claims;
} catch (ExpiredJwtException e) {
throw new JWTException("令牌已过期", e);
} catch (UnsupportedJwtException e) {
throw new JWTException("不支持的令牌类型", e);
} catch (MalformedJwtException e) {
throw new JWTException("令牌格式错误", e);
} catch (SignatureException e) {
throw new JWTException("签名验证失败", e);
}
}
}
令牌刷新机制
// 令牌刷新服务
@Service
public class TokenRefreshService {
private final JWTTokenBuilder tokenBuilder;
private final RefreshTokenStore refreshTokenStore;
private final JWTValidator validator;
public TokenPair refreshTokens(String refreshToken) {
// 验证刷新令牌
Claims claims = validator.validateToken(refreshToken);
// 检查是否为刷新令牌
if (!"refresh".equals(claims.get("token_type"))) {
throw new SecurityException("无效的刷新令牌");
}
String tokenId = claims.getId();
String userId = claims.getSubject();
// 验证刷新令牌是否存在且未使用
RefreshToken storedToken = refreshTokenStore.get(tokenId);
if (storedToken == null || storedToken.isUsed()) {
// 可能存在令牌盗用,撤销所有相关令牌
revokeAllUserTokens(userId);
throw new SecurityException("刷新令牌已被使用,可能存在安全风险");
}
// 标记当前刷新令牌为已使用
refreshTokenStore.markAsUsed(tokenId);
// 生成新的令牌对
User user = userService.findById(userId);
String newAccessToken = tokenBuilder.createAccessToken(user,
Arrays.asList(claims.get("scope").toString().split(" ")));
RefreshToken newRefreshToken = tokenBuilder.createRefreshToken(user);
// 存储新的刷新令牌
refreshTokenStore.save(newRefreshToken);
return TokenPair.builder()
.accessToken(newAccessToken)
.refreshToken(newRefreshToken.getToken())
.expiresIn(3600)
.build();
}
private void revokeAllUserTokens(String userId) {
refreshTokenStore.revokeAllForUser(userId);
blacklistService.blacklistAllForUser(userId);
}
}
安全最佳实践
# JWT安全配置
jwt:
security:
# 密钥管理
key_rotation:
enabled: true
interval: 90d
algorithm: RS256
# 令牌配置
access_token:
lifetime: 15m
header: Authorization
prefix: "Bearer "
refresh_token:
lifetime: 7d
rotation: true
absolute_lifetime: 30d
reuse_detection: true
# 安全选项
options:
prevent_replay: true
require_https: true
validate_claims: true
check_blacklist: true
JWT架构通过合理的令牌结构设计、安全的刷新机制和完善的防护措施,确保身份验证系统的可靠性和安全性。