JWT签名密钥必须全局复用,不可每次请求重生成;密钥应作为常量或从环境变量加载;RSA需私钥签发、公钥验证且配对稳定;用户信息须用c.Set()绑定上下文,勿操作Request;exp应基于UTC时间设置并显式赋值;刷新token需access+refresh双机制及服务端存储校验。

JWT签名密钥必须全局复用,不能每次请求都重生成
很多人在 Gin 中集成 JWT 时,把 jwt.NewSigningMethodHS256() 和密钥生成逻辑写在 handler 里,结果每次验证都失败。JWT 验证依赖服务端用同一套密钥签名和校验,密钥一旦不一致,token is invalid 或 signature is invalid 就会频繁报出。
- 密钥应作为常量或从环境变量加载,例如:
var jwtSecret = []byte(os.Getenv("JWT_SECRET")) - 不要在中间件里调用
jwt.New()创建新 token 实例来“重新签发”,验证阶段只需用jwt.Parse+ 同一密钥 - 若使用 RSA,私钥只用于签发(login 接口),公钥用于验证(其他接口),二者必须配对且稳定
Gin context 绑定用户信息要用 c.Set(),别直接改 c.Request
JWT 解析成功后,需要把用户 ID、角色等信息透传给后续 handler。常见错误是往 c.Request.Context() 里塞值,或者试图修改 c.Request.Header —— 这些操作要么无效,要么污染原始请求。
- 正确做法:在 JWT 验证中间件里调用
c.Set("user_id", userID)和c.Set("role", role) - 下游 handler 用
userID, ok := c.Get("user_id")取值,ok必须检查,避免 panic - 切勿用
c.Request.Header.Set()存用户数据,Header 是 HTTP 协议层,不是业务上下文载体
time.Now().Add(time.Hour * 24) 不等于 “24 小时后”,要注意时区和系统时间漂移
JWT 的 exp 字段是 Unix 时间戳(秒级),Gin 本身不处理时区,但 time.Now() 默认用本地时区。如果服务器时钟不准,或跨时区部署,会导致 token 提前过期或长期有效。
- 统一用 UTC 时间:改用
time.Now().UTC().Add(time.Hour * 24) - 生成 token 时显式设置
exp:用claims["exp"] = exp.Unix(),别依赖库自动填充 - 验证时,
jwt.Parse默认校验exp,但若传入jwt.WithoutClaimsValidation()就会跳过,务必确认没误关
刷新 token 要双 token(access + refresh)+ 独立存储,不能只延长 access token 过期时间
单纯把 access token 的 exp 改长,等于放弃“短期 token + 定期刷新”的安全设计。攻击者一旦窃取旧 token,就能一直用下去。
立即学习“go语言免费学习笔记(深入)”;
- 标准做法:login 返回两个 token —— 短期
access_token(15–30 分钟),长期refresh_token(7–30 天) -
refresh_token必须存服务端(如 Redis),绑定 user_id + fingerprint(UA + IP 哈希),且用完即删、单次有效 - 刷新接口要校验
refresh_token是否存在、未被撤销,并生成全新access_token,而不是修改旧 token 的exp
JWT 本身无状态,但刷新机制必须有状态支撑;漏掉存储或校验环节,整个无状态设计就变成假无状态。


















