token验证中间件必须挂载在路由注册之前,全局用app.use(authmiddleware),分组用admin := app.party("/admin").use(authmiddleware),不可补挂于app.get()之后。

Token验证中间件该挂在哪一层
Iris 的中间件必须注册在路由组或具体 handler 之前,不能在 app.Get() 之后补挂。常见错误是把中间件写在路由定义之后,导致压根不执行。
- 中间件要通过
iris.Handler类型函数传入,比如app.Use(authMiddleware)(全局)或admin := app.Party("/admin", authMiddleware)(分组) - 如果只对部分接口鉴权,优先用
Party+ 中间件组合,避免在每个 handler 里重复校验 - 不要试图在
ctx.Next()后再取ctx.Values().Get("user")并做逻辑判断——那说明中间件没提前完成解析和注入
如何从 Authorization Header 提取并解析 JWT
Iris 默认不带 JWT 解析能力,得靠第三方库如 github.com/golang-jwt/jwt/v5。关键不是“怎么解”,而是“解错时怎么快速失败”。
- Header 格式必须是
Authorization: Bearer <token></token>,少空格、多空格、写成Bearer:<token></token>都会导致ctx.Request().Header.Get("Authorization")取不到有效值 - 解析前先检查 token 字符串长度(
len(token) 可直接 <code>ctx.StopWithStatus(iris.StatusUnauthorized)) - 使用
jwt.ParseWithClaims(token, &MyClaims{}, keyFunc),其中keyFunc必须返回非 nil 错误才能中断流程;返回nil, nil是常见坑点
func authMiddleware(ctx iris.Context) {
authHeader := ctx.Request().Header.Get("Authorization")
if authHeader == "" {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
parts := strings.Fields(authHeader)
if len(parts) != 2 || parts[0] != "Bearer" {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
token, err := jwt.ParseWithClaims(parts[1], &UserClaims{}, func(t *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWT_SECRET")), nil
})
if err != nil || !token.Valid {
ctx.StopWithStatus(iris.StatusUnauthorized)
return
}
claims := token.Claims.(*UserClaims)
ctx.Values().Set("user_id", claims.UserID)
ctx.Next()
}
为什么 ctx.StopWithStatus 比 ctx.StatusCode + return 更可靠
Iris 的中间件链是顺序执行的,一旦某个中间件调用了 ctx.Next(),后续中间件和最终 handler 就一定会运行——哪怕你刚在上一步写了 ctx.StatusCode(iris.StatusUnauthorized)。
-
ctx.StopWithStatus()内部会调用ctx.StopExecution(),强制终止整个链,这是唯一能确保“鉴权失败就不进业务逻辑”的方式 - 单纯
ctx.StatusCode()+ctx.JSON()只是发响应,但执行流还会往下走,可能触发 DB 查询、日志记录甚至副作用操作 - 如果你还用了自定义错误处理中间件(比如
app.OnErrorCode(iris.StatusUnauthorized, ...)),它不会覆盖已发出的响应体,反而可能造成双写 panic
如何安全地把用户信息透传给下游 handler
不要在中间件里直接修改全局变量或用 map 存用户 ID,Iris 提供了 ctx.Values() 这个线程安全的请求作用域存储。
-
ctx.Values().Set("user_id", id)和ctx.Values().Get("user_id")是配对使用的,类型断言需谨慎:v, ok := ctx.Values().Get("user_id").(int64) - 不要用
ctx.Set()或ctx.Params()传用户数据,前者是用于模板渲染,后者只读且来自 URL 路径 - 如果下游 handler 需要频繁访问用户信息,建议封装一个辅助函数:
func GetUser(ctx iris.Context) (*User, bool) { ... },在里面统一做类型检查和 DB 查询缓存
实际项目中容易忽略的是 token 过期时间校验粒度——开发时常用 time.Now().Add(24 * time.Hour) 硬编码,上线后必须对接 NTP 时间源,否则集群节点时钟偏差会导致大量 Token is expired 报错。











