# JWT企业级加密 - 快速参考卡 ## 🔑 密钥管理 ### 生成新密钥对 ```bash .\generate-jwt-keys.ps1 ``` ### 配置密钥 ```properties jwt.public-key=YOUR_PUBLIC_KEY_BASE64 jwt.private-key=YOUR_PRIVATE_KEY_BASE64 jwt.expiration=86400000 ``` --- ## 💻 代码速查 ### 生成Token ```java // 基础用法 String token = JwtUtil.generateToken(userId, username); // 带额外声明 Map claims = new HashMap<>(); claims.put("role", "admin"); String token = JwtUtil.generateToken(userId, username, claims); ``` ### 验证Token ```java // 基础验证 boolean isValid = JwtUtil.validateToken(token); // 完整验证(含黑名单) boolean isValid = JwtUtil.validateToken(token, blacklistService); ``` ### 提取信息 ```java String userId = JwtUtil.getUserIdFromToken(token); String username = JwtUtil.getUsernameFromToken(token); String jti = JwtUtil.getJtiFromToken(token); long remainingTime = JwtUtil.getTokenRemainingTime(token); // 秒 ``` ### Token注销 ```java String jti = JwtUtil.getJtiFromToken(token); long remainingTime = JwtUtil.getTokenRemainingTime(token); blacklistService.addToBlacklist(jti, remainingTime); ``` ### 刷新Token ```java 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已被注销 | --- ## 🔧 密钥轮换 ```java // 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黑名单 ```java // 加入黑名单 blacklistService.addToBlacklist(jti, expirationSeconds); // 检查是否在黑名单中 boolean isBlacklisted = blacklistService.isBlacklisted(jti); // 获取黑名单大小 long size = blacklistService.getBlacklistSize(); ``` --- ## 🎯 拦截器模板 ```java @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 ```javascript // 推荐:HttpOnly Cookie(后端设置) // Set-Cookie: token=xxx; HttpOnly; Secure; SameSite=Strict // 或:LocalStorage(简单场景) localStorage.setItem('token', token); ``` ### 发送请求 ```javascript // Axios axios.get('/api/v1/profile', { headers: { 'Authorization': `Bearer ${token}` } }); // Fetch fetch('/api/v1/profile', { headers: { 'Authorization': `Bearer ${token}` } }); ``` ### 处理401错误 ```javascript axios.interceptors.response.use( response => response, error => { if (error.response?.status === 401) { // Token过期或无效,跳转登录页 window.location.href = '/login'; } return Promise.reject(error); } ); ``` --- ## 🔍 调试技巧 ### 查看Token内容(不验证) ```java 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](https://jwt.io/) 粘贴Token即可查看 --- ## ⚡ 性能优化 ```properties # 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_ENTERPRISE_GUIDE.md) **代码示例**: [JWT_USAGE_EXAMPLES.md](JWT_USAGE_EXAMPLES.md)