jwt中exp字段必须写入秒级unix时间戳整数,如time.now().add(1*time.hour).unix(),不可用毫秒或time.time类型;验证时需区分jwt.errtokenexpired等具体错误,避免混淆签名失效与过期。

Token过期时间怎么写进JWT payload里
JWT标准里用 exp 字段表示过期时间,必须是 Unix 时间戳(秒级整数),不是字符串也不是毫秒值。Gin本身不生成JWT,得靠第三方库(比如 golang-jwt/jwt)手动构造。常见错误是直接传 time.Now().Add(1 * time.Hour) 这种 time.Time 类型——会 panic,因为 exp 要求 int64。
正确做法是调用 .Unix() 方法转换:
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"user_id": 123,
"exp": time.Now().Add(1 * time.Hour).Unix(), // ✅
})
-
exp值一旦写入就不可更改,验证时由库自动比对系统时间 - 别用
time.Now().UnixMilli(),JWT规范只认秒级 - 如果服务时区不统一,确保所有节点系统时间已用 NTP 同步,否则可能提前失效
Gin中间件里怎么校验Token是否过期
用 jwt.Parse 解析后,它内部会自动检查 exp、nbf(生效时间)、iat(签发时间),但默认行为是:只要任一时间校验失败,就返回 *jwt.ValidationError,且 err.Error() 里含具体原因,比如 "token is expired" 或 "token not active yet"。
别跳过错误类型判断,直接用 if err != nil 就返回 401 —— 这样会把签名错误和过期混为一谈,不利于调试:
token, err := jwt.Parse(tokenString, keyFunc)
if err != nil {
if errors.Is(err, jwt.ErrTokenExpired) {
c.AbortWithStatusJSON(401, gin.H{"error": "token expired"})
return
}
c.AbortWithStatusJSON(401, gin.H{"error": "invalid token"})
return
}
-
jwt.ErrTokenExpired是导出的 error 变量,可直接比较 - 若用老版本
github.com/dgrijalva/jwt-go,需用errors.As(err, &e)提取*jwt.ValidationError再查e.Errors & jwt.ValidationErrorExpired - 解析成功不代表 token 有效:还要检查
token.Valid == true,否则可能绕过时间校验
如何让过期Token在Redis里自动清理
单纯依赖 JWT 的 exp 不够——用户登出或密钥轮换后,已签发的 token 仍能用到过期为止。需要加一层“黑名单”机制,而 Redis 的 EXPIRE 是最匹配的:把 token 的 jti(唯一标识)作为 key,过期时间设为和 JWT 一致。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
关键点在于:不能等 token 过期才写入 Redis,得在签发时就同步写,并设置 TTL:
jwtToken, _ := token.SignedString(key) jti := getJTI(token) // 通常从 claims 里取或生成 UUID redisClient.Set(ctx, "blacklist:"+jti, "1", time.Until(expTime)) // expTime 是你算出的 time.Time
- 验证中间件里先查
redisClient.Get(ctx, "blacklist:"+jti),命中即拒绝,不管 JWT 是否未过期 - 别用
SET key val EX seconds手动拼命令,Go 客户端如redis-go的Set方法自带Expiration参数更安全 - 如果 token 没带
jti,至少用sha256(tokenString)作 key,避免明文存储敏感 token
时区和系统时间不同步导致验证失败怎么办
JWT 时间校验基于服务器本地时间,如果机器时间慢了 2 分钟,一个刚签发的 token 就会被判定为 “not active yet”;快了 2 分钟,则可能提前报 expired。这不是代码 bug,而是基础设施问题。
验证逻辑本身没法绕过这个限制,只能做两件事:
- 所有运行 Gin 服务的节点必须启用 NTP(如
systemd-timesyncd或chrony),并定期检查偏移量:timedatectl status - 在解析 JWT 时,可给
time.Now()加几秒宽松窗口(不推荐),例如修改jwt.Parser{ValidateExp: true}的底层行为——但会削弱安全性,仅用于临时兼容旧设备 - 真正要监控的是:日志里高频出现
"token not active yet"或"token used before issued",基本就是时间不同步信号
时间校准这事不在 Go 代码里解决,写再多 time.Add 都没用。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










