gin 默认不带 jwt 认证因其专注路由与中间件,认证方式多样故未内置;需手动集成 golang-jwt/jwt(非已归档且有漏洞的 jwt-go),并实现含上下文传值、错误拦截与 exp 显式校验的中间件。

为什么 Gin 默认不带 JWT 认证,得自己集成
Gin 本身只提供路由和中间件机制,jwt-go 或 golang-jwt/jwt 这类库才是实际做 token 签发与校验的。官方没内置 JWT,是因为认证方式太多(session、OAuth2、API Key),强行绑定反而限制灵活性。你得手动选库、写中间件、处理错误响应——这不是 Gin 的缺陷,而是它的设计取舍。
用 golang-jwt/jwt 而不是 dgrijalva/jwt-go
dgrijalva/jwt-go 已归档且存在已知安全问题(如 CVE-2020-26160),新项目必须用维护中的 golang-jwt/jwt。它 API 有变化:比如 ParseWithClaims 第二个参数要传指针,SigningMethodHS256 变成 jwt.SigningMethodHS256,且默认不自动验证 exp 字段,得显式调用 VerifyExpiresAt。
- 安装命令是:
go get -u github.com/golang-jwt/jwt/v5 - 签发时用
jwt.NewWithClaims(jwt.SigningMethodHS256, claims),不是旧版的jwt.NewTokenWithClaims - 校验后务必检查
token.Valid,且手动调用claims.VerifyExpiresAt(time.Now().UTC(), true),否则过期 token 仍可能被接受
写一个可复用的 JWT 中间件,注意上下文传值和错误拦截
中间件不能只做校验,还要把解析出的用户 ID 或角色塞进 c.Request.Context(),下游 handler 才能取用;同时得统一返回 401,避免每个接口都重复写错误响应逻辑。
- 从
Authorization: Bearer <token></token>头里提取 token,别用 query 或 form —— 安全性差且不符合惯例 - 解析失败时直接调用
c.AbortWithStatusJSON(401, map[string]string{"error": "invalid token"}),别 return - 用
c.Set("user_id", uint(claims.(jwt.MapClaims)["user_id"].(float64)))传值,注意类型断言要安全,建议封装成结构体 claims - 密钥必须从环境变量或配置文件读,绝不能硬编码在代码里,比如
os.Getenv("JWT_SECRET")
登录接口签发 token 时,别漏掉关键字段和安全设置
签发 token 不只是拼个 user_id,还得控制生命周期、防止重放、适配前端存储习惯。
- 必须设
exp(过期时间),建议用time.Now().Add(24 * time.Hour).Unix(),别用固定数字 - 加
iat(签发时间)和nbf(生效时间),方便调试和未来扩展 - 前端若存 localStorage,记得提醒它 XSS 风险;若存 httpOnly cookie,需额外配置
SetCookie并关闭SameSite限制(跨域场景) - 响应里除了
token字段,最好附带expires_at时间戳,方便前端做本地过期判断
Gin 的 JWT 实现难点不在语法,而在密钥管理、上下文传递一致性、以及过期/刷新/吊销这些真实业务场景的衔接。很多人卡在 token 解析后不知道怎么把 user_id 透传给 handler,或者忽略了 exp 字段必须手动验证——这两点不处理,上线后就是线上事故。











