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

275 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 企业级JWT加密方案使用指南
## 📋 概述
本项目已升级到**企业级RSA-256 JWT加密方案**,包含以下特性:
### ✅ 核心功能
- 🔐 **RSA-256非对称加密** - 私钥签名,公钥验证
- 🔄 **密钥轮换机制** - 支持无缝切换密钥
- 🛡️ **防重放攻击** - JTI + Redis黑名单
- 📝 **增强Claims** - ISS/AUD/NBF/JTI完整验证
- ⚠️ **分类异常处理** - 精确的错误类型识别
---
## 🚀 快速开始
### 1. 生成RSA密钥对
运行密钥生成工具:
```bash
# 方式1: 直接运行Java类
java -cp target/classes com.caiji.uls.utils.jwt.RsaKeyGenerator
# 方式2: 运行测试类(推荐)
.\mvnw.cmd test-compile exec:java -Dexec.mainClass="com.caiji.uls.KeyGenTest"
```
生成的输出类似:
```
=== 生成RSA密钥对 ===
公钥 (Public Key):
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
私钥 (Private Key):
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC...
=== 请将以上密钥配置到 application.properties ===
jwt.public-key=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
jwt.private-key=MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC...
```
### 2. 配置密钥
将生成的密钥复制到 `src/main/resources/application.properties`
```properties
# JWT Configuration (Enterprise RSA-256)
jwt.public-key=你的公钥Base64字符串
jwt.private-key=你的私钥Base64字符串
jwt.expiration=86400000
```
### 3. 启动应用
```bash
.\mvnw.cmd spring-boot:run
```
启动成功后会看到:
```
[JWT] RSA密钥配置已初始化
[JWT] 过期时间: 86400000 毫秒 (1440 分钟)
[JWT] 签名算法: RS256 (RSA-SHA256)
```
---
## 💡 使用示例
### 生成Token
```java
import com.caiji.uls.utils.jwt.JwtUtil;
// 基础用法
String token = JwtUtil.generateToken(userId, username);
// 带额外声明
Map<String, Object> claims = new HashMap<>();
claims.put("role", "admin");
claims.put("department", "IT");
String token = JwtUtil.generateToken(userId, username, claims);
```
### 验证Token
```java
import com.caiji.uls.utils.jwt.JwtUtil;
import com.caiji.uls.utils.jwt.TokenBlacklistService;
@Autowired
private TokenBlacklistService blacklistService;
// 基础验证(仅验证签名和格式)
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);
```
### Token注销(加入黑名单)
```java
@Autowired
private TokenBlacklistService blacklistService;
// 获取Token的JTI
String jti = JwtUtil.getJtiFromToken(token);
// 计算剩余有效期(秒)
long remainingTime = JwtUtil.getTokenRemainingTime(token);
// 加入黑名单
blacklistService.addToBlacklist(jti, remainingTime);
```
---
## 🔧 高级功能
### 密钥轮换
```java
import com.caiji.uls.utils.jwt.JwtKeyManager;
// 生成新密钥对
var newKeyPair = RsaKeyGenerator.generateKeyPair();
String newPublicKey = RsaKeyGenerator.encodePublicKey((RSAPublicKey) newKeyPair.getPublic());
String newPrivateKey = RsaKeyGenerator.encodePrivateKey((RSAPrivateKey) newKeyPair.getPrivate());
// 执行轮换(旧密钥仍可用于验证)
JwtKeyManager.rotateKeys(newPublicKey, newPrivateKey);
// 过渡期后清除旧密钥(建议7-30天后)
JwtKeyManager.clearPreviousKey();
```
### 自定义Claims验证
```java
import io.jsonwebtoken.Claims;
Claims claims = JwtUtil.getClaimsFromToken(token);
// 获取标准字段
String issuer = claims.getIssuer(); // 签发者
String audience = claims.getAudience(); // 受众
Date issuedAt = claims.getIssuedAt(); // 签发时间
Date expiration = claims.getExpiration(); // 过期时间
// 获取自定义字段
String userId = claims.get("userId", String.class);
String role = claims.get("role", String.class);
```
---
## 🛡️ 安全特性
### 1. RSA-256签名
- **私钥签名**:只有认证服务器持有私钥
- **公钥验证**:可分发公钥给多个微服务
- **防篡改**:任何修改都会导致签名验证失败
### 2. Claims增强
| 字段 | 说明 | 用途 |
|------|------|------|
| `iss` | 签发者 | 防止伪造Token |
| `aud` | 受众 | 限制Token使用范围 |
| `nbf` | Not Before | 防止时钟偏移攻击 |
| `jti` | JWT ID | 唯一标识,防重放 |
| `iat` | Issued At | 签发时间追踪 |
| `exp` | Expiration | 自动过期 |
### 3. Redis黑名单
- **即时注销**:用户登出后立即失效
- **密码修改保护**:修改密码后所有Token失效
- **自动清理**:Redis TTL自动删除过期条目
### 4. 异常分类
| 异常类型 | HTTP状态码 | 说明 |
|----------|-----------|------|
| `JwtTokenExpiredException` | 401 | Token已过期 |
| `JwtSignatureInvalidException` | 401 | 签名无效 |
| `JwtMalformedException` | 400 | 格式错误 |
| `JwtTokenBlacklistedException` | 401 | 已被注销 |
---
## ⚙️ 配置项说明
```properties
# RSA公钥(Base64编码)
jwt.public-key=YOUR_PUBLIC_KEY
# RSA私钥(Base64编码,务必保密!)
jwt.private-key=YOUR_PRIVATE_KEY
# Token过期时间(毫秒),默认24小时
jwt.expiration=86400000
```
---
## 📊 架构优势对比
| 特性 | 旧版(HMAC | 新版(RSA |
|------|-------------|------------|
| 加密算法 | HS256对称加密 | RS256非对称加密 |
| 密钥管理 | 单密钥硬编码 | 密钥对+轮换机制 |
| 多服务支持 | ❌ 需共享密钥 | ✅ 只需分发公钥 |
| 防重放攻击 | ❌ 无 | ✅ JTI+黑名单 |
| Claims验证 | 基础 | ISS/AUD/NBF完整验证 |
| 密钥泄露风险 | ⚠️ 高 | ✅ 低(私钥不泄露) |
| 企业合规 | ❌ 不符合 | ✅ 符合OWASP标准 |
---
## 🔍 故障排查
### 问题1: 启动时提示"未检测到RSA密钥配置"
**原因**: `application.properties`中未配置密钥
**解决**: 运行密钥生成工具并配置密钥
### 问题2: Token验证失败"签名无效"
**原因**: 密钥不匹配或Token被篡改
**解决**:
1. 确认公钥/私钥配置正确
2. 检查Token是否完整传输
3. 查看日志中的Key ID是否匹配
### 问题3: Redis连接失败
**原因**: Redis服务未启动或配置错误
**解决**: 检查`application.properties`中的Redis配置
---
## 📚 相关文件
- **密钥生成**: [RsaKeyGenerator.java](file:///D:/ClassDev/Spring/uni_login_system/src/main/java/com/caiji/uls/utils/jwt/RsaKeyGenerator.java)
- **密钥管理**: [JwtKeyManager.java](file:///D:/ClassDev/Spring/uni_login_system/src/main/java/com/caiji/uls/utils/jwt/JwtKeyManager.java)
- **JWT工具**: [JwtUtil.java](file:///D:/ClassDev/Spring/uni_login_system/src/main/java/com/caiji/uls/utils/jwt/JwtUtil.java)
- **黑名单服务**: [TokenBlacklistService.java](file:///D:/ClassDev/Spring/uni_login_system/src/main/java/com/caiji/uls/utils/jwt/TokenBlacklistService.java)
- **配置初始化**: [JwtConfigInitializer.java](file:///D:/ClassDev/Spring/uni_login_system/src/main/java/com/caiji/uls/config/JwtConfigInitializer.java)
- **异常处理**: [JwtGlobalExceptionHandler.java](file:///D:/ClassDev/Spring/uni_login_system/src/main/java/com/caiji/uls/config/JwtGlobalExceptionHandler.java)
---
## 🎯 最佳实践
1. **生产环境必须配置固定密钥对**,不要使用自动生成的临时密钥
2. **私钥严格保密**,不要提交到版本控制系统
3. **定期轮换密钥**(建议每30-90天)
4. **监控黑名单大小**,异常增长可能表示安全问题
5. **设置合理的过期时间**,平衡安全性和用户体验
6. **HTTPS传输**,防止Token被中间人窃取
---
## 📞 技术支持
如有问题,请检查:
1. 应用启动日志中的JWT初始化信息
2. Redis连接状态
3. 密钥配置是否正确
4. Token格式是否符合JWT标准(三段式Base64)