必须使用 github.com/golang-jwt/jwt/v5,因其修复了 CVE-2023-37582 等签名绕过漏洞;v4 及旧版 jwt-go 存在真实安全风险,且 v5 强制显式指定算法与密钥、校验标准声明、遵循 RFC 6750 的 Bearer 传输规范,并需自行实现注销机制。

必须用 github.com/golang-jwt/jwt/v5,别碰 v4 或已归档的 jwt-go —— CVE-2023-37582 等签名绕过漏洞在旧版中真实存在,不是理论风险。
生成 Token 时密钥和算法必须显式指定
很多人写完 token.SignedString([]byte("secret")) 就以为万事大吉,结果上线后 token 总是解析失败或被绕过。根本原因在于:v5 不再接受隐式算法推断,也不容忍弱密钥。
-
SigningMethodHS256必须作为参数传给jwt.NewWithClaims(),不能靠反射猜 - 密钥必须是
[]byte,且长度 ≥32 字节(HS256 最低要求),硬编码"secret"或 16 字节字符串等于公开邀请爆破 - 不要用
time.Now().UnixMilli()填exp—— v5 按秒解析,毫秒值会导致 token 被判定为“公元 5 万年后过期”,实际立刻失效 - 推荐写法:
ExpiresAt: jwt.NewNumericDate(time.Now().Add(2 * time.Hour)),类型安全、单位明确
Parse 时必须校验标准声明字段,不能只看签名
只调用 jwt.Parse[MyClaims](tokenStr, keyFunc) 并检查 err == nil 是典型裸奔操作。签名正确 ≠ token 可用 —— 攻击者可重放旧 token、伪造 issuer、或利用未设 iat 的时间窗口。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 自定义 claims 结构体必须嵌入
jwt.RegisteredClaims,否则Validate()不生效 - 验证逻辑里必须显式调用
claims.Validate(),它会自动检查exp、iat、nbf、iss、aud -
keyFunc回调中应校验token.Header["alg"]是否等于"HS256",拒绝"none"或未知算法 - 建议加
WithValidMethods([]string{"HS256"}),从解析器层面堵死非预期算法
Gin 中间件提取 token 必须走 Authorization: Bearer 标准头
客户端把 token 塞进 query、cookie、X-Auth-Token 甚至 body 里,服务端还兼容?这不是灵活,是主动绕开所有网关和 WAF 的鉴权规则。
立即学习“go语言免费学习笔记(深入)”;
- RFC 6750 明确规定 bearer token 只能通过
Authorization: Bearer <token></token>传输 - Kong、Apigee、AWS API Gateway 等默认只识别该 header,自定义 header 会导致鉴权链路断裂
- 中间件提取逻辑应严格匹配正则
^Bearer\s+(.+)$,空格和大小写都要对得上 - 解析成功后,用
context.WithValue(r.Context(), "userID", claims.Subject)注入上下文,后续 handler 直接取,别重复解析
最容易被忽略的点:JWT 本身不提供注销能力。如果用户登出或密码重置,你没在 Redis 里维护一个短时效的 blacklist 或改用 refresh token 机制,那个 token 就一直有效——直到自然过期。这不是库的问题,是你没补上那一层状态管理。

















