Jwt验证原理
阅读时间:
4
min 文章字数:
878
字 发布日期:
2025-10-29
最近更新:
2025-10-31
阅读量:
-
JWT通过数字签名机制来验证是否被篡改
Jwt结构
JWT由3个部分组成,用.连接,分别如下:
- header
- Payload
- Signature
markdown
头部.载荷.签名Jwt签名生产过程
- 获取header和payload,将header和payload进行base64编码,生成header和payload
- 组成待签名的数据,将header和payload进行base64编码,生成header和payload,并拼接起来
- 使用密钥和指定算法(示例中使用HMAC_SHA512)生成签名
- 将签名进行base64Url编码,生成签名并与待签名的数据进行拼接,生成完整的JWT
javascript
// 1. 对头部和载荷进行Base64Url编码
const encodedHeader = base64UrlEncode(header);
const encodedPayload = base64UrlEncode(payload);
// 2. 组合成待签名的数据
const signingInput = encodedHeader + "." + encodedPayload;
// 3. 使用密钥和指定算法生成签名
const signature = HMAC_SHA512(secretKey, signingInput);
// 4. 组合成完整的JWT
const jwt = signingInput + "." + base64UrlEncode(signature);Jwt验签过程
- 分割JWT的3个部分
- 服务端根据密钥和指定算法重新计算签名
- 最后比较签名,如果相同则验证成功
java
public boolean verifyJWT(String jwt, String secretKey) {
// 1. 分割JWT的三个部分
String[] parts = jwt.split("\\.");
String encodedHeader = parts[0];
String encodedPayload = parts[1];
String receivedSignature = parts[2];
// 2. 重新计算签名
String signingInput = encodedHeader + "." + encodedPayload;
String computedSignature = base64UrlEncode(
HMAC_SHA512(secretKey, signingInput)
);
// 3. 比较签名(使用恒定时间比较防止时序攻击)
return MessageDigest.isEqual(
computedSignature.getBytes(),
receivedSignature.getBytes()
);
}扩展
在生产过程中我们一般会赋予每一个JWT过期时间,减少令牌被盗用的风险(即使令牌泄露,攻击者也只能在有限时间内使用)
1.增强安全性
java
// 减少令牌被盗用的风险
// 即使令牌泄露,攻击者也只能在有限时间内使用
if (jwt.getExpiration().before(new Date())) {
throw new SecurityException("令牌已过期,请重新登录");
}2.强制重新认证
java
// 定期要求用户重新验证身份
// 确保用户状态仍然有效(如账户未被禁用)
public void checkUserStatus(String userId) {
User user = userService.findById(userId);
if (!user.isActive()) {
throw new AccountDisabledException("用户账户已被禁用");
}
}3.会话管理
java
// 控制用户会话时长
// 符合安全策略要求(如金融应用要求15分钟无操作自动退出)
@Value("${jwt.timeout.short:900}") // 15分钟
private int shortLivedTimeout;
@Value("${jwt.timeout.long:86400}") // 24小时
private int longLivedTimeout;4.资源保护
java
// 防止长期占用服务器资源
// 自动清理过期的会话数据
@Scheduled(fixedRate = 3600000) // 每小时清理一次
public void cleanupExpiredTokens() {
tokenRepository.deleteByExpirationBefore(new Date());
}5.密钥轮换支持
java
// 定期更换签名密钥时,过期的JWT会自动失效
// 不需要额外的撤销机制
public void rotateSigningKey() {
// 新密钥立即生效
currentKey = generateNewKey();
// 旧密钥签发的JWT会在过期后自动失效
}常见的过期时间策略
1.访问令牌 (Access Token)
yaml
# 短期令牌 - 高安全性
short_lived:
duration: 15分钟 # 900秒
适用场景: 银行、支付、敏感操作
# 中等期限 - 平衡体验与安全
medium_lived:
duration: 2小时 # 7200秒
适用场景: 普通Web应用、移动应用
# 长期令牌 - 用户体验优先
long_lived:
duration: 24小时 # 86400秒
适用场景: 信任设备、内部系统2.刷新令牌 (Refresh Token)
java
// 刷新令牌可以有更长的生命周期
// 用于获取新的访问令牌
@Bean
public JwtEncoder jwtEncoder() {
// 访问令牌: 2小时过期
JwtClaimsSet accessTokenClaims = JwtClaimsSet.builder()
.expiresAt(Instant.now().plus(2, ChronoUnit.HOURS))
.build();
// 刷新令牌: 7天过期
JwtClaimsSet refreshTokenClaims = JwtClaimsSet.builder()
.expiresAt(Instant.now().plus(7, ChronoUnit.DAYS))
.build();
}