权限验证必须在BeforeActivation中为特定路由显式注册中间件;JWT解析失败后须调用ctx.StopExecution()终止流程,仅返回401响应不足以阻止后续执行;权限数据应存入ctx.Values()而非全局变量;RBAC校验需作为独立中间件,在JWT中间件之后注册。

权限验证必须在BeforeActivation里注册中间件
Iris MVC不自动注入鉴权逻辑,控制器方法本身不会拦截请求,所有权限检查必须通过BeforeActivation显式挂载到具体路由上。如果你只在BeginRequest里做校验,它会在所有方法前执行,但无法阻止非法请求进入业务逻辑——因为BeginRequest之后仍会继续调用目标方法。
正确做法是在BeforeActivation中对特定方法绑定中间件:
-
b.Handle("GET", "/user/{id:long}", "GetByID", authMiddleware)—— 只对GetByID加鉴权 - 不要写
b.Router().Use(authMiddleware),那会作用于整个Party,过度覆盖 - 中间件内部必须调用
ctx.StopExecution()终止流程,仅ctx.StatusCode(401)+ctx.JSON()不够,后续方法仍会执行
JWT解析失败后必须StopExecution,不能只写响应
常见错误是JWT中间件里解析token失败后只调用了ctx.JSON(401, ...),却忘了ctx.StopExecution()。Iris不会自动中断控制流,ctx.Next()之后的代码(包括控制器方法体)照常运行,导致权限绕过。
典型错误写法:
func authMiddleware(ctx iris.Context) {
token := ctx.GetHeader("Authorization")
if !valid(token) {
ctx.StatusCode(401)
ctx.JSON(iris.Map{"code": 40001, "msg": "未授权"})
// ❌ 缺少 ctx.StopExecution(),接下来仍会进 GetByID
}
ctx.Next()
}
正确写法:
- 校验失败 →
ctx.StatusCode(401)+ctx.JSON(...)+ctx.StopExecution() - 校验成功 → 调用
ctx.Next()放行 - 别在中间件末尾无条件写
ctx.Next(),应严格用if/else分叉
权限数据需从Context取,不能依赖全局变量
用户角色、权限列表等上下文数据必须存入ctx.Values(),并在后续中间件或控制器中用ctx.Values().Get("role")读取。若用包级变量(如var currentUser *User),在并发请求下会相互覆盖,造成权限错乱。
例如,在JWT中间件中:
- 解析出
userID和roles后,存入:ctx.Values().Set("user_id", userID)、ctx.Values().Set("roles", roles) - 权限校验中间件再取:
roles := ctx.Values().GetStringSlice("roles") - 控制器里也可取:
userID := ctx.Values().GetInt64("user_id")
注意:ctx.Values()生命周期仅限单次请求,安全且隔离。
RBAC校验要放在独立中间件,避免耦合控制器
不要把if !hasPermission("user:read")硬编码在GetByID里。应提取为可复用的中间件,比如requirePermission("user:read"),再按需挂载。
这样做的好处:
- 权限规则集中管理,改一处全生效
- 不同控制器复用同一中间件,避免重复判断逻辑
- 测试时可单独 mock 权限中间件,不用启动完整 HTTP 流程
容易被忽略的一点:RBAC中间件必须在JWT解析中间件之后注册,否则ctx.Values()里还没有roles,校验会永远失败。


















