5.9 KiB
5.9 KiB
JWT企业级加密 - 快速参考卡
🔑 密钥管理
生成新密钥对
.\generate-jwt-keys.ps1
配置密钥
jwt.public-key=YOUR_PUBLIC_KEY_BASE64
jwt.private-key=YOUR_PRIVATE_KEY_BASE64
jwt.expiration=86400000
💻 代码速查
生成Token
// 基础用法
String token = JwtUtil.generateToken(userId, username);
// 带额外声明
Map<String, Object> claims = new HashMap<>();
claims.put("role", "admin");
String token = JwtUtil.generateToken(userId, username, claims);
验证Token
// 基础验证
boolean isValid = JwtUtil.validateToken(token);
// 完整验证(含黑名单)
boolean isValid = JwtUtil.validateToken(token, blacklistService);
提取信息
String userId = JwtUtil.getUserIdFromToken(token);
String username = JwtUtil.getUsernameFromToken(token);
String jti = JwtUtil.getJtiFromToken(token);
long remainingTime = JwtUtil.getTokenRemainingTime(token); // 秒
Token注销
String jti = JwtUtil.getJtiFromToken(token);
long remainingTime = JwtUtil.getTokenRemainingTime(token);
blacklistService.addToBlacklist(jti, remainingTime);
刷新Token
String newToken = JwtUtil.refreshToken(oldToken);
🛡️ Claims字段
| 字段 | 说明 | 示例 |
|---|---|---|
jti |
JWT唯一ID | "5f3e2a8b-1b4d-4e..." |
iss |
签发者 | "uni-login-system" |
aud |
受众 | "uni-login-client" |
sub |
主题(用户名) | "admin" |
iat |
签发时间 | 1716624000 |
nbf |
生效时间 | 1716623995 |
exp |
过期时间 | 1716710400 |
userId |
用户ID(自定义) | "123" |
⚠️ 异常类型
| 异常类 | HTTP码 | 触发条件 |
|---|---|---|
JwtTokenExpiredException |
401 | Token已过期 |
JwtSignatureInvalidException |
401 | 签名验证失败 |
JwtMalformedException |
400 | Token格式错误 |
JwtTokenBlacklistedException |
401 | Token已被注销 |
🔧 密钥轮换
// 1. 生成新密钥对
KeyPair newKeyPair = RsaKeyGenerator.generateKeyPair();
String newPublicKey = RsaKeyGenerator.encodePublicKey((RSAPublicKey) newKeyPair.getPublic());
String newPrivateKey = RsaKeyGenerator.encodePrivateKey((RSAPrivateKey) newKeyPair.getPrivate());
// 2. 执行轮换(旧密钥仍可用于验证)
JwtKeyManager.rotateKeys(newPublicKey, newPrivateKey);
// 3. 过渡期后清除旧密钥(建议7-30天后)
JwtKeyManager.clearPreviousKey();
📊 Redis黑名单
// 加入黑名单
blacklistService.addToBlacklist(jti, expirationSeconds);
// 检查是否在黑名单中
boolean isBlacklisted = blacklistService.isBlacklisted(jti);
// 获取黑名单大小
long size = blacklistService.getBlacklistSize();
🎯 拦截器模板
@Component
public class JwtAuthenticationInterceptor implements HandlerInterceptor {
@Autowired
private TokenBlacklistService blacklistService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) {
String authHeader = request.getHeader("Authorization");
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new JwtMalformedException("缺少认证令牌");
}
String token = authHeader.substring(7);
if (!JwtUtil.validateToken(token, blacklistService)) {
throw new JwtSignatureInvalidException("令牌无效");
}
request.setAttribute("userId", JwtUtil.getUserIdFromToken(token));
request.setAttribute("username", JwtUtil.getUsernameFromToken(token));
return true;
}
}
📝 前端使用
存储Token
// 推荐:HttpOnly Cookie(后端设置)
// Set-Cookie: token=xxx; HttpOnly; Secure; SameSite=Strict
// 或:LocalStorage(简单场景)
localStorage.setItem('token', token);
发送请求
// Axios
axios.get('/api/v1/profile', {
headers: {
'Authorization': `Bearer ${token}`
}
});
// Fetch
fetch('/api/v1/profile', {
headers: {
'Authorization': `Bearer ${token}`
}
});
处理401错误
axios.interceptors.response.use(
response => response,
error => {
if (error.response?.status === 401) {
// Token过期或无效,跳转登录页
window.location.href = '/login';
}
return Promise.reject(error);
}
);
🔍 调试技巧
查看Token内容(不验证)
Claims claims = Jwts.parser()
.build()
.parseSignedClaims(token)
.getPayload();
System.out.println("JTI: " + claims.getId());
System.out.println("Subject: " + claims.getSubject());
System.out.println("Expires: " + claims.getExpiration());
在线解码
访问 jwt.io 粘贴Token即可查看
⚡ 性能优化
# Redis连接池
spring.data.redis.lettuce.pool.max-active=20
spring.data.redis.lettuce.pool.max-idle=10
spring.data.redis.lettuce.pool.min-idle=5
🚫 安全禁忌
❌ 不要在日志中打印完整Token
❌ 不要在前端LocalStorage存储敏感Token
❌ 不要硬编码私钥到代码中
❌ 不要忽略Token验证异常
❌ 不要在HTTP下传输Token
✅ 始终使用HTTPS
✅ 使用HttpOnly Cookie存储
✅ 定期轮换密钥
✅ 设置合理的过期时间
✅ 监控黑名单增长
📞 常见问题
Q: Token验证失败?
A: 检查公钥/私钥是否匹配,确认Token未损坏
Q: 如何使所有Token失效?
A: 修改密码后将该用户的所有JTI加入黑名单
Q: 密钥多久轮换一次?
A: 建议30-90天,高安全要求可缩短至7天
Q: Redis宕机怎么办?
A: 降级为基础验证(仅验证签名),记录告警
详细文档: JWT_ENTERPRISE_GUIDE.md
代码示例: JWT_USAGE_EXAMPLES.md