Bootstrap

踩坑:设置过期时间为常量

 问题代码:

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天后)。

问题原因

  1. 静态常量EXPIRATION_TIME 被定义为静态常量,它在类加载时设置,之后不再改变。
  2. 固定的过期时间: 每次调用 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,避免将其硬编码在源代码中。您可以使用环境变量、配置文件或密钥管理服务来管理密钥。
;