jwt时间校验需显式启用jwt.withvalidtime()并检查token.valid;refresh token须独立传输通道(如x-refresh-token)、分离密钥、redis单次有效存储及前端promise共享并发刷新。

jwt.Parse 必须显式启用时间校验
很多人发现 token.Valid 总是 true,哪怕 payload 里写了 "exp"——因为 jwt.Parse 默认跳过时间验证。不加选项,token.Claims 中的 ExpiresAt 字段也不会被自动解析或校验。
正确写法是传入 jwt.WithValidTime():
token, err := jwt.Parse(tokenString, keyFunc, jwt.WithValidTime())
之后必须手动判断:if err != nil || !token.Valid,不能只看 err。否则所有过期 Token 都会被当作有效放行。
refreshToken 必须走独立 Header,且密钥分离
共用 Authorization: Bearer xxx 或同名 Cookie(如 token)会导致中间件无法区分两种 Token:要么全按 Access Token 解析失败,要么误把 Refresh Token 当 Access Token 放行,引发权限越界。
必须显式使用独立传输通道:
- 前端发 Refresh 请求时,用
X-Refresh-TokenHeader 传值 - 服务端用
c.Request.Header.Get("X-Refresh-Token")显式提取 -
accessSecret和refreshSecret必须是两个不同变量,不可复用同一环境变量或常量 -
ParseToken函数需带isRefresh bool参数,内部据此切换密钥与 Claims 结构
别用 jwt.MapClaims 做通用解析——它绕过结构体字段校验,容易因字段缺失或类型错位导致静默失败。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
Redis 中 refreshToken 必须单次有效 + 即时失效
JWT 本身无吊销能力。若 refresh token 纯无状态(比如只签个 long-exp 字符串),泄露即等于永久通行证。
Redis Key 格式建议:refresh:{jti},值为 {user_id}:{fingerprint_hash}(fingerprint_hash 推荐用 sha256(fmt.Sprintf("%s:%s", r.UserAgent(), realIP)) 生成)。
验证流程必须严格:
- 先
GET,命中则立刻DEL(强制单次使用) - 再生成新对并写入新
jti - 新
refreshToken的exp设为 7 天,但 Redis TTL 必须严格同步该时间 - 刷新接口返回的新
refreshToken必须立即生效,旧 key 在 Redis 中已不可查——最易被忽略的是:很多实现只写入新值,却忘了删旧 key
前端 Promise 共享是并发刷新的唯一解法
点一次按钮触发多个并发请求,全卡在 401 边界。如果每个都调 /auth/refresh,服务端会生成多对新 Token,前端无法确定哪个有效,Authorization header 覆盖混乱,后续请求大概率 403。
必须维护一个全局变量 refreshPromise:
- 首次 401 时触发刷新并缓存 Promise
- 后续同批 401 全部
await refreshPromise,而非发新请求 - 刷新成功后,用新
accessToken重放原始请求;失败则清空登录态(Cookie 或内存 token)
长连接(WebSocket/gRPC)场景下,服务端需主动预推送新 Token(如 exp 前 5 分钟),不能等过期才响应。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










