Files
Uni_Login_system/doc/JWT_QUICK_REFERENCE.md
2026-05-31 04:46:30 +08:00

263 lines
5.9 KiB
Markdown

# 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<String, Object> 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)