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
相关产品推荐
相关产品推荐

