JWT令牌安全实践详解
一、JWT概述
JSON Web Token(JWT)是一种用于安全传输信息的开放标准(RFC 7519)。
1.1 JWT结构
复制代码
┌─────────────────────────────────────────────────────────────┐
│ JWT Token │
├─────────────────────────────────────────────────────────────┤
│ Header.Payload.Signature │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ {"alg":"HS256", {"sub":"123", HMACSHA256( │
│ "typ":"JWT"} "name":"John", base64Url(header). │
│ "exp":1704067200) + "." + │
│ base64Url(payload),│
│ secret) │
└─────────────────────────────────────────────────────────────┘
1.2 JWT组成部分
| 部分 |
说明 |
编码方式 |
| Header |
算法和类型 |
Base64Url |
| Payload |
声明信息 |
Base64Url |
| Signature |
签名 |
HMAC/RSA |
二、JWT实现
2.1 创建JWT
java
复制代码
import io.jsonwebtoken.*;
import java.util.Date;
public class JwtUtil {
private static final String SECRET_KEY = "your-256-bit-secret-key";
private static final long EXPIRATION_TIME = 86400000; // 24小时
public static String generateToken(String userId, String username) {
return Jwts.builder()
.setSubject(userId)
.claim("username", username)
.claim("role", "admin")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
}
2.2 验证JWT
java
复制代码
public static Claims validateToken(String token) {
try {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
} catch (JwtException | IllegalArgumentException e) {
throw new RuntimeException("Invalid token", e);
}
}
2.3 刷新Token
java
复制代码
public static String refreshToken(String token) {
Claims claims = validateToken(token);
claims.setIssuedAt(new Date());
claims.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME));
return Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
三、JWT安全配置
3.1 密钥管理
java
复制代码
// 使用256位以上的密钥
private static final String SECRET_KEY = generateSecureKey();
private static String generateSecureKey() {
SecureRandom random = new SecureRandom();
byte[] key = new byte[32]; // 256 bits
random.nextBytes(key);
return Base64.getEncoder().encodeToString(key);
}
3.2 使用RSA非对称加密
java
复制代码
// 生成RSA密钥对
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA");
keyGen.initialize(2048);
KeyPair keyPair = keyGen.generateKeyPair();
// 使用私钥签名
String token = Jwts.builder()
.setSubject("user123")
.signWith(keyPair.getPrivate(), SignatureAlgorithm.RS256)
.compact();
// 使用公钥验证
Claims claims = Jwts.parser()
.setSigningKey(keyPair.getPublic())
.parseClaimsJws(token)
.getBody();
3.3 设置合理的过期时间
java
复制代码
// 访问Token:短过期时间
private static final long ACCESS_TOKEN_EXPIRE = 15 * 60 * 1000; // 15分钟
// 刷新Token:长过期时间
private static final long REFRESH_TOKEN_EXPIRE = 7 * 24 * 60 * 60 * 1000; // 7天
四、安全最佳实践
4.1 Token存储策略
| 存储位置 |
优点 |
缺点 |
适用场景 |
| LocalStorage |
方便访问 |
XSS风险 |
单页应用 |
| SessionStorage |
会话级存储 |
页面切换丢失 |
临时数据 |
| HttpOnly Cookie |
XSS安全 |
CSRF风险 |
传统Web |
| Memory |
最安全 |
页面刷新丢失 |
高安全场景 |
4.2 CSRF防护
java
复制代码
// Spring Security配置
http.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers("/api/**")
);
4.3 XSS防护
html
复制代码
<!-- 设置HttpOnly和Secure标志 -->
Set-Cookie: JWT=token; HttpOnly; Secure; SameSite=Strict
五、Token黑名单机制
5.1 基于Redis的黑名单
java
复制代码
public class TokenBlacklist {
private final StringRedisTemplate redisTemplate;
private static final String PREFIX = "blacklist:";
public void invalidateToken(String token, long expireSeconds) {
String key = PREFIX + token;
redisTemplate.opsForValue().set(key, "true", expireSeconds, TimeUnit.SECONDS);
}
public boolean isBlacklisted(String token) {
String key = PREFIX + token;
return Boolean.TRUE.equals(redisTemplate.hasKey(key));
}
}
5.2 拦截器验证
java
复制代码
public class JwtInterceptor implements HandlerInterceptor {
private final TokenBlacklist blacklist;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = extractToken(request);
if (blacklist.isBlacklisted(token)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
// 继续验证token
return true;
}
}
六、JWT vs Session对比
| 特性 |
JWT |
Session |
| 状态 |
无状态 |
有状态 |
| 存储 |
客户端 |
服务端 |
| 扩展性 |
好 |
差 |
| 安全性 |
需额外处理 |
相对安全 |
| 性能 |
网络传输大 |
服务器内存 |
七、安全检查清单
7.1 必做检查
| 检查项 |
说明 |
| 使用HTTPS |
防止Token被窃取 |
| 设置过期时间 |
限制Token有效期 |
| 避免敏感信息 |
Payload是Base64编码,不是加密 |
| 使用强密钥 |
256位以上密钥 |
| 验证签名 |
防止Token被篡改 |
7.2 推荐检查
| 检查项 |
说明 |
| 实现刷新机制 |
定期轮换Token |
| 实现黑名单 |
支持主动注销 |
| 限制Token大小 |
避免过大Payload |
| 监控异常行为 |
检测暴力破解 |
八、总结
JWT是一种强大的身份认证机制,但需要正确使用才能保证安全:
- 使用HTTPS:始终通过HTTPS传输Token
- 合理设置过期时间:访问Token短,刷新Token长
- 使用非对称加密:避免密钥泄露风险
- 实现黑名单机制:支持主动注销
- 存储在安全位置:根据场景选择存储方式
通过以上措施,可以构建安全可靠的JWT认证系统。