正确写法是:中间件必须校验authorization头格式、解析失败立即返回401、成功后将claims注入context并调用next.servehttp;刷新需绑定设备指纹、redis黑名单校验jti、accesstoken≤15分钟、refreshtoken设ttl+偏移。

JWT 中间件怎么写才不会绕过认证
Go 微服务里加 JWT 认证,最常踩的坑是中间件没校验 Authorization 头、没提前 return、或在 handler 里又重复解析 token。结果是某些路由看似有中间件,实际被跳过。
正确做法是:中间件必须检查 header 是否存在且格式合法(Bearer <token></token>),解析失败立即 http.Error 并返回 401;成功后把 userID 或 claims 注入 context.Context,后续 handler 从 context 取,不自己再解析。
- 别用
strings.Split(r.Header.Get("Authorization"), " ")然后取索引 1 —— 如果 header 是空或只有Bearer,会 panic - 用
jwt.ParseWithClaims时务必传入自定义jwt.Claims结构体,别用jwt.MapClaims,否则字段类型不一致导致user_id变成float64 - 中间件最后必须调用
next.ServeHTTP(w, r),漏掉这句整个链路就断了
如何安全地验证和刷新 token
单纯校验签名和过期时间远远不够。生产环境必须做三件事:验证 iss(issuer)、比对 aud(audience)、检查 nbf(not before);刷新逻辑要绑定设备指纹或 IP 段,不能只靠 refresh token 本身。
示例中常见错误是把 RefreshToken 存在 cookie 里却没设 HttpOnly 和 SameSite=Strict,或在响应头里直接返回新 AccessToken 而不撤销旧 token。
-
AccessToken过期时间建议 ≤ 15 分钟,RefreshToken存 Redis 并设 TTL + 随机偏移(防批量续期) - 刷新接口必须校验旧
RefreshToken的jti字段,并在 Redis 中删除它,再发新 pair - 别在 JWT payload 里放密码哈希、权限列表等易变字段 —— 改权限就得换 token,应查 DB 或缓存
微服务间调用怎么透传和校验 JWT
服务 A 调 B 时,如果只是简单把原始 Authorization 头转发过去,B 就无法区分是终端用户直连还是内部调用 —— 权限模型会混乱。
推荐方案:所有出向 HTTP 请求统一用 context.WithValue 注入 auth.Token,封装在 client 工具函数里自动加头;服务 B 的中间件增加 X-Internal-Call 校验(仅限内网 IP 或 mTLS 证书),并允许白名单 service name 做降级校验(比如只验 iss 不验 aud)。
- 别在 grpc metadata 里用
map[string]string直接塞 raw token —— grpc 默认不校验长度,超长 token 触发grpc: received message larger than max - gateway 层(如 krakend、envoy)若做了 JWT 解析,后端服务仍需独立校验,不能盲目信任
X-User-ID这类透传 header - service mesh(如 istio)的 mTLS 和 JWT 是两套机制,不能互相替代:mTLS 保传输,JWT 保身份
Redis 黑名单和 key 设计容易被忽略的细节
JWT 本质无状态,但注销、踢人必须依赖外部存储。很多人用 DEL jwt:blacklist:<jti></jti> 就以为完事了,结果发现删不掉 —— 因为 key 过期时间没对齐,或 jti 生成方式不唯一。
真正可用的黑名单方案:以 jti 为 key,value 存 exp 时间戳(单位秒),TTL 设为 exp - time.Now().Unix();查询时用 EXISTS + GET 双检,避免误判已过期但未自动清理的 key。
-
jti必须全局唯一且不可预测 —— 别用uuid.NewV4().String(),而应组合userID + timestamp + rand(8) - Redis key 命名建议带环境前缀,例如
prod:jwt:blacklist:<jti></jti>,避免 dev/staging 冲突 - 如果用 Redis Cluster,确保
jti落在同一个 slot —— 加大括号,如{prod:jwt:blacklist:<jti></jti>
JWT 的签名强度、密钥轮换、时钟偏差容忍这些点,往往在压测或跨时区部署时才暴露。别等到线上 401 暴增才去翻 time.Now().Unix() - claims.IssuedAt > 5 这种硬编码。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











