You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang中JWT令牌存入Redis时过期时间不符合预期问题

问题分析与修复方案

核心问题1:全局过期时间变量初始化逻辑错误

你定义的jwtRefreshExpiration和jwtAccessExpiration是全局变量,程序启动时就执行了time.Now().Add(...)计算出固定时间值,后续生成令牌时不会重新计算当前时间加上期时长。更关键的是,GenerateToken函数完全忽略了传入的expiration参数,硬编码使用全局变量,导致两种令牌的Redis过期时间都被固定为全局变量的值。

核心问题2:JWT的exp字段格式错误

JWT标准要求exp字段为Unix时间戳(秒),但你直接传入了time.Time类型的值,这会导致JWT验证逻辑失效,同时也不符合规范。

额外问题:AccessToken的claims键名拼写错误

GenerateAccessToken中的claims键是"AccesssToken"(多了一个s),虽然当前分支判断能匹配,但属于低级错误,容易引发后续逻辑问题。


修复后的完整代码

1. 修正全局变量定义(移除固定过期时间,改为常量定义时长)

var(
    redisDb         = redis_jwt.RedisData{}
    redisController = rediscontroller.New(&redisDb)
    signKey         = os.Getenv("SigningKey")
)

const (
    refreshTokenExpire = 30 * 24 * time.Hour // 30天
    accessTokenExpire  = 15 * time.Minute    // 按需求改为15分钟
)

2. 修正GenerateToken函数,使用传入的过期时间参数

// GenerateToken generates a jwt token with the specified claims and expiration time
func GenerateToken(claims jwt.MapClaims, expiration time.Time) (string, error) {
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    tokenStr, err := token.SignedString([]byte(signKey))
    if err != nil {
        return "", fmt.Errorf("Couldn't sign token: %v", err)
    }
    if id, ok := claims["RefreshToken"].(string); ok {
        // 使用传入的expiration,而非全局固定值
        err = redisController.SaveToken(redisDto.RedisDto{Token: tokenStr, Key: "RefreshToken", Id: id, Expiration: expiration})
        if err != nil {
            return "", fmt.Errorf("Couldn't save refresh token to Redis: %v", err)
        }
    } else if id, ok := claims["AccessToken"].(string); ok { // 修正拼写错误
        // 使用传入的expiration,而非全局固定值
        err = redisController.SaveToken(redisDto.RedisDto{Token: tokenStr, Key: "AccessToken", Id: id, Expiration: expiration})
        if err != nil {
            return "", fmt.Errorf("Couldn't save access token to Redis: %v", err)
        }
    }
    return tokenStr, nil
}

3. 修正令牌生成函数,动态计算过期时间并规范JWT的exp字段

// GenerateRefreshToken generates a refresh token for the specified user ID
func GenerateRefreshToken(userId string) (string, error) {
    expiration := time.Now().Add(refreshTokenExpire)
    claims := jwt.MapClaims{
        "RefreshToken": userId,
        "exp":          expiration.Unix(), // 转换为Unix时间戳(秒)
    }
    return GenerateToken(claims, expiration)
}

// GenerateAccessToken generates an access token for the specified user ID
func GenerateAccessToken(userId string) (string, error) {
    expiration := time.Now().Add(accessTokenExpire)
    claims := jwt.MapClaims{
        "AccessToken": userId, // 修正拼写错误
        "exp":          expiration.Unix(), // 转换为Unix时间戳(秒)
    }

    return GenerateToken(claims, expiration)
}

4. Redis存储逻辑的优化建议

你的Redis存储用userId作为Hash的Key,设置该Key的过期时间会导致:如果用户先生成30天有效期的refresh token,再生成15分钟的access token,Hash的过期时间会被覆盖为15分钟,导致refresh token提前失效。建议:

  • 将refresh token和access token存到独立的Redis Key下,比如refresh_token:{userId}和access_token:{userId},各自设置独立过期时间;
  • 若坚持用Hash存储,可通过额外Key记录每个字段的过期时间,或使用RedisJSON等支持字段级过期的模块。

内容的提问来源于stack exchange,提问作者Folium

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 15:39:54