refresh token 必须带服务端生成的 uuid jti 并强校验、隔离传输通道、独立密钥、清旧 cookie、前端共享刷新 promise、长连接预刷新原子切换——漏任一环将导致 401/403 混乱或令牌无限续期。

Go 里实现 Refresh Token 不是加个 /auth/refresh 接口就完事——漏掉 jti 校验、复用密钥、不清旧 Cookie、前端不共享 Promise,上线后必出 401/403 混乱或令牌被无限续期。
Refresh Token 必须带 jti 且服务端强校验
很多开发者用 jwt.MapClaims 塞 user_id 和 exp 就返回,结果 refresh_token 泄露后无法主动作废。jti 不是可选字段,它是防重放的唯一凭证。
-
jti必须由服务端生成 UUID(不能客户端传、不能拼时间戳),解析时从 payload 提取后立即查 Redis:GET refresh_token:{jti} - 值格式建议为
user_id:fingerprint_hash,不匹配或 key 不存在直接拒掉 - 查完立刻
DEL refresh_token:{jti},这一步漏掉等于开放无限刷新后门 - 别在刷新逻辑里再查用户表——refresh_token 本身应自包含
sub和jti,查库引入竞态且拖慢响应
Access Token 和 Refresh Token 必须隔离传输与密钥
共用 Authorization: Bearer xxx 或同一个 Cookie 名(比如都叫 token),中间件根本分不清是哪种 token,轻则解析失败,重则把 refresh_token 当 access_token 验证,权限越界。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- Refresh Token 必须走独立通道:HTTP Header 用
X-Refresh-Token,Cookie 名必须是refresh_token - 登录/刷新成功后,务必先清旧 Cookie:
c.SetCookie("refresh_token", "", -1, "/", "example.com", true, true) - accessSecret 和 refreshSecret 必须不同变量,
ParseToken函数得带isRefresh bool参数来切换密钥和 Claims 结构 - Access Token 的 Claims 只需
sub和短exp(如 15 分钟);Refresh Token 的 Claims 必须含jti和长exp(如 7 天)
前端必须共享 refresh Promise,不能各自重试
一个按钮触发 3 个并发请求,全部收到 401 后各自调 /auth/refresh,结果只有最后一个响应生效,前两个新 token 被丢弃但 Authorization header 已被覆盖——后续请求全 403。
- 所有请求统一经过拦截器,遇到 401 先检查是否存在正在执行的
refreshPromise - 有则 await 它,没则发起刷新,并把 Promise 缓存到全局变量(如
window.refreshPromise或 Vuex store) - 刷新成功后用新 token 重放原始请求;失败才清本地凭证(Cookie 或内存 token)
- 别在拦截器里二次解析 request body 取 token——只信任已解析过的
token.Claims["jti"],否则并发下可能因顺序错乱导致双签发
长连接场景下不能依赖 HTTP 响应头刷新
WebSocket/gRPC 握手后无法再发 Set-Cookie 或新 Authorization header,等 token 过期再处理就是断连。服务端必须主动推、客户端必须原子切。
- 每个连接启动 goroutine,用
time.Until(claims.ExpiresAt.Time.Add(-5*time.Minute))触发预刷新 - 推送控制帧如
{"type": "token_refresh_hint", "at_exp": 1747928880},避免被业务消息误解析 - 客户端收到后先
jwt.Parse()校验新 access_token,再原子替换当前 token,不能直接覆盖 - refresh_token 必须随控制帧单独传,payload 含
jti和fingerprint_hash,服务端收第一件事就是DEL refresh:{jti}
真正难的不是生成新 token,而是让旧 token 在毫秒级失效、让并发请求串行化、让长连接里 token 切换不中断业务——这些细节一旦松动,安全边界就塌了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










