修正项目结构
This commit is contained in:
@@ -0,0 +1,262 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user