鉴权中间件应解析Authorization头、校验JWT签名与过期时间,区分expired和invalid错误,将用户ID存入c.Set;跳过路由需用分组隔离而非路径判断;密钥类型、token格式及时间戳精度须严格匹配;refresh token逻辑应由业务handler按需处理。

怎么写一个 Gin 的鉴权中间件校验 JWT Token
直接用 gin.HandlerFunc 拦住请求,在解析 Authorization header 后调用 JWT 库验证签名和过期时间。别在中间件里做数据库查用户——那是业务逻辑,应该放到 handler 里按需加载。
常见错误是把 token 解析失败当成「未登录」统一返回 401,但其实 token expired 和 invalid signature 应该区分对待:前者可提示刷新,后者得拒之门外。
- 从
c.Request.Header.Get("Authorization")取值,手动切掉"Bearer "前缀(注意空格) - 用
jwt.Parse配合自定义Keyfunc验证签名,别硬编码密钥 - 检查
err == nil且token.Valid为 true,二者缺一不可 - 验证通过后,把用户 ID 或
*jwt.Token存进c.Set("user_id", uid),供后续 handler 读取
Gin 中间件怎么跳过某些路由(比如登录接口)
Gin 本身不提供“跳过中间件”的语法糖,得靠路由分组 + 手动挂载。把不需要鉴权的接口(如 /login、/healthz)放在独立的 gin.RouterGroup,不加中间件;需要鉴权的才用 authorized := r.Group("/api", authMiddleware)。
别试图在中间件里写一堆 if c.Request.URL.Path == "/login" 判断——路径匹配容易漏掉带参数的路由(比如 /users/:id),也难维护。
立即学习“go语言免费学习笔记(深入)”;
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 登录、注册、静态资源、Swagger 页面这些,一律走无中间件的 group
- 如果必须动态跳过(比如某类用户免鉴权),应在中间件内部查 DB 或缓存,而不是靠 URL 匹配
- 注意
router.Use()是全局的,一旦用了就无法对单个路由取消,只能分组隔离
为什么 token 校验总报 Key is not valid 或 signature is invalid
90% 是密钥类型或格式没对上:jwt.Parse 要求的密钥类型取决于你签发时用的算法。用 HS256 就传 []byte,用 RS256 就得传 *rsa.PublicKey,混用必炸。
另一个坑是 token 字符串里带换行或空格——前端 JS 用 localStorage 存的时候可能多塞了空白符,后端没 trim 就直接 parse,会触发 malformed token。
- 签发用
HS256,验证就用func(token *jwt.Token) (interface{}, error) { return []byte(yourSecret), nil } - 验证前先
strings.TrimSpace(tokenStr),再检查是否以"Bearer "开头 - 用
token.Claims.(jwt.MapClaims)["exp"]读过期时间时,记得转成int64再比对,Go 的 float64 时间戳容易精度丢失
如何让鉴权中间件支持 Refresh Token 流程
Refresh Token 不该在鉴权中间件里自动续期——那会让所有请求都偷偷刷新,埋下并发冲突和滥用风险。正确做法是:鉴权中间件只管「当前请求是否合法」;当 handler 发现 token 即将过期(比如 exp - time.Now().Unix() ),再单独调用刷新接口返回新 token。
强行在中间件里刷新会导致两个问题:一是响应体被中间件篡改(你没法往原 response 插 header),二是刷新失败时不知道该返回什么状态码——401?403?还是 200 带新 token?语义全乱了。
- 刷新逻辑放在专门的
POST /auth/refresh接口里,由前端主动触发 - 鉴权中间件只做一件事:校验 token 有效性,有效就放行,无效就
c.AbortWithStatusJSON(401, ...) - 如果要透传过期预警,可以在
c.Set("warn_expire", true),让 handler 自己决定是否附带新 token 在响应 header 里
最麻烦的其实是 token 黑名单和多端踢出——Gin 中间件本身不带状态,得靠 Redis 记 token_jti 或 user_id + device_id,而且每次验证都要查一次 Redis。这点很容易被忽略,等上线才发现性能扛不住。

















