问题代码:
package com.sundark.mylife.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.function.Function;
/**
* @author wujiada
* jwt生成token
*/
public class JwtUtil {
private static final String SECRET_KEY = "wujiada";
private static final long EXPIRATION_TIME = System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 7;
/**
* <p>获取用户id</p>
*
* @param token 用户token存了用户id
* @author wujiada
* @since 2024/8/5 09:41
*/
public static String extractUserId(String token) {
return extractClaim(token, Claims::getSubject);
}
/**
* <p>判断token是否失效</p>
*
* @param token 用户token
* @author wujiada
* @since 2024/8/5 09:40
*/
private static Boolean isTokenExpired(String token) {
return extractExpiration(token).before(new Date());
}
/**
* <p>生成token</p>
*
* @param userId 用户id
* @author wujiada
* @since 2024/8/5 09:39
*/
public static String generateToken(String userId) {
return Jwts.builder()
.setSubject(userId)
.setIssuedAt(new Date(System.currentTimeMillis()))
.setExpiration(new Date(EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
// 验证token
public static String isValidToken(String token) {
if (StringUtil.isEmpty(token) || isTokenExpired(token)) {
// 判断是否过期
return null;
} else {
return extractUserId(token);
}
}
public static Date extractExpiration(String token) {
return extractClaim(token, Claims::getExpiration);
}
public static <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
final Claims claims = extractAllClaims(token);
return claimsResolver.apply(claims);
}
private static Claims extractAllClaims(String token) {
return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
}
}
JwtUtil
类中,EXPIRATION_TIME
是一个静态常量,它在类加载到 JVM 时被初始化一次,并且之后不会再改变。这意味着无论您何时生成新的 token,所有的 token 都会有相同的过期时间,即从服务器启动或类被加载时起的固定时间(在这个例子中是7天后)。
问题原因
- 静态常量:
EXPIRATION_TIME
被定义为静态常量,它在类加载时设置,之后不再改变。 - 固定的过期时间: 每次调用
generateToken
时,都会使用相同的EXPIRATION_TIME
值,而不是基于当前时间计算一个新的过期时间。
解决方案
要解决这个问题,您应该让 EXPIRATION_TIME
成为动态计算的,而不是静态常量。您可以在 generateToken
方法中直接计算过期时间,而不是使用一个固定的值。这样,每次生成的 token 都将有一个新的过期时间。
修改后的 generateToken
方法
public static String generateToken(String userId) {
long currentTimeMillis = System.currentTimeMillis();
long expirationTimeMillis = currentTimeMillis + 1000 * 60 * 60 * 24 * 7; // 7天后过期
return Jwts.builder()
.setSubject(userId)
.setIssuedAt(new Date(currentTimeMillis))
.setExpiration(new Date(expirationTimeMillis))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
在这个修改后的版本中,expirationTimeMillis 是在每次调用 generateToken 时计算的,基于当前的时间加上7天的毫秒数。这样,每次生成的 token 都会有一个不同的过期时间,基于它们被生成的时间。
其他注意事项
- 确保您的服务器时间和客户端时间同步,以避免由于时间差异导致的 token 过期问题。
- 如果您的应用是多实例部署的,确保所有实例都使用相同的
SECRET_KEY
,以便能够验证由其他实例生成的 token。 - 考虑使用更安全的方式来存储和管理
SECRET_KEY
,避免将其硬编码在源代码中。您可以使用环境变量、配置文件或密钥管理服务来管理密钥。