双令牌必须分离设计,因access token需短时效(15–30分钟)、无状态校验,refresh token需长时效(7天)、绑定jti并存redis实现一次性使用;共用jwt.newwithclaims易致密钥混用、缺乏jti、重放攻击等安全风险。

为什么不能只用 jwt.NewWithClaims 生成刷新令牌
刷新令牌(Refresh Token)和访问令牌(Access Token)必须分离设计,核心原因是安全策略不同:访问令牌要短时效、无状态校验;刷新令牌需长时效、必须绑定用户设备或会话特征,且每次使用后应立即失效(one-time use)。如果用同一套 jwt.NewWithClaims 逻辑生成两者,极易忽略签名密钥隔离、过期时间硬编码、以及刷新令牌缺乏唯一性标识(如 jti)等问题,导致重放攻击风险。
实操建议:
- 为刷新令牌单独定义结构体,强制包含
jti(UUID)、user_id、issued_at和较长的exp(例如 7 天) - 访问令牌的
exp控制在 15–30 分钟,且不存入数据库;刷新令牌的jti必须写入 Redis,设置与过期时间一致的 TTL - 不要复用同一个
SigningKey—— 生产环境建议用不同密钥:访问令牌用access_signing_key,刷新令牌用refresh_signing_key
如何在 Gin 中安全地签发并返回双令牌
Gin 的 c.SetCookie 是最常用但最容易出错的返回方式。直接把刷新令牌塞进 HttpOnly Cookie 而不设 SameSite=Strict 或验证 User-Agent/IP,等于敞开大门。
实操建议:
- 访问令牌走响应体 JSON:
{"access_token": "xxx", "expires_in": 900} - 刷新令牌走
SetCookie,但必须显式指定:Path="/refresh"、HttpOnly=true、Secure=true(HTTPS 环境)、SameSite=httprouter.SameSiteStrictMode - 禁止在 Cookie 中传访问令牌;也禁止把刷新令牌放在响应体里返回(避免前端意外泄露)
- 签发时调用
jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims)后,立即用tokenString, _ := token.SignedString(refreshSignKey)生成字符串,并存入 Redis:redisClient.Set(ctx, "rt:"+refreshClaims.JTI, user.ID, 7*24*time.Hour)
POST /refresh 接口必须校验哪些字段
这个接口是整个机制最薄弱的一环。常见错误是只校验 JWT 签名和 exp,却忽略 jti 是否已用、是否匹配当前用户、是否被篡改。
实操建议:
- 从 Cookie 读取刷新令牌(
c.Cookie("refresh_token")),而非请求体或 Header —— 防止前端误暴露 - 解析时必须校验
Valid()+VerifySignature(用refresh_signing_key),再手动检查claims["jti"]是否存在且为字符串 - 查 Redis:
redisClient.Get(ctx, "rt:"+jti).Result(),若返回空或不是当前用户 ID,则直接拒绝,不返回任何提示(防枚举) - 校验通过后,立刻删除旧
jti:redisClient.Del(ctx, "rt:"+jti),再生成新访问令牌(含新exp)和新刷新令牌(含新jti)
前端怎么配合才不会让刷新逻辑崩掉
前端不是被动接收方,而是机制闭环的关键一环。很多问题其实源于前端没处理好并发请求或错误状态。
实操建议:
- 所有 API 请求统一加拦截器:当响应返回
401且响应头含X-Need-Refresh: true时,才触发刷新流程;不要靠响应体字段判断 - 刷新请求必须串行化:用 Promise lock(如
let refreshingPromise = null)确保同一时间只发一个/refresh,其余请求等待其完成 - 刷新失败(如 401 或网络错误)后,清空本地存储的 token 并跳转登录页 —— 不要重试,也不要在 UI 上静默失败
- 不要在 Axios 的
interceptors.request里自动附带刷新令牌;它只该出现在/refresh请求中
刷新机制真正难的不是签发或解析,而是状态一致性:Redis 的 jti 生命周期、前端请求队列管理、以及服务端对“已使用”刷新令牌的原子性校验。漏掉任意一环,都会导致令牌复用或无限 401 循环。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











