必须用permissionaspect(aop切面)替代纯http中间件,因中间件无法可靠获取用户角色信息导致rbac失效;aop可精准注入角色并遍历校验,配合keymatch2与正确g规则实现动态权限控制。

Hyperf 中用中间件 + Casbin 实现 RBAC 权限控制,核心不是“能不能”,而是“策略加载时机”和“请求上下文能否拿到用户角色”。直接上结论:必须用 PermissionAspect(AOP 切面)替代纯中间件,否则 casbin_enforcer 拿不到当前登录用户的 role 信息,RBAC 就会失效。
为什么不能只靠 HTTP 中间件做 RBAC 鉴权
Hyperf 的 HTTP 中间件在路由匹配后、控制器执行前运行,但此时 $request->getAttribute('user') 往往为空——除非你手动在前置中间件里解析 token 并 setAttribute。而 Casbin 的 enforce() 调用依赖 sub(即用户或角色标识),RBAC 模型里这个 sub 必须是角色名(如 admin),不是用户 ID。
常见错误现象:
-
enforce('1001', '/user', 'GET') === false—— 因为传的是用户 ID,但模型里定义的是g, admin, user:read - 策略表
casbin_rules里写的是p, admin, /user, GET,但中间件传了1001,永远不匹配 - 强行在中间件里查 DB 补全角色,性能差、易出竞态、破坏无状态原则
正确做法:用 AOP 切面拦截注解 + 动态注入角色
Hyperf 的 AOP 在方法执行前可精确获取到当前用户上下文(比如从 JWT 解析后的 user 对象),再通过 PermissionService 查出其所有角色,最后调用 enforcer->enforce($role, $resource, $action)。
实操关键点:
- 注解值必须是资源+操作组合,例如
#[Permission('user:delete')],不能写成路径/user/delete,否则和 Casbin 模型里的keyMatch2不对齐 -
PermissionAspect中需显式调用$container->get(PermissionService::class)->getRolesByUserId($userId),不能假设单角色 - 模型配置中
[matchers]必须启用keyMatch2,否则不支持user:*这类通配符 - 若用数据库存策略,确保
casbin.php中'type' => 'database'且表名与迁移一致(默认casbin_rules)
RBAC 模型文件里 g 规则怎么写才生效
很多人卡在角色继承没生效,本质是 g 行格式或数据写错了。Casbin 的 g 是「角色属于某组」,不是「用户属于某角色」。
正确写法(casbin_rbac_model.conf):
[role_definition] g = _, _
对应数据库策略表数据应为:
p, admin, user:delete, allow g, alice, admin g, bob, editor
注意:
-
g行第一列是「主体」(用户或子角色),第二列是「目标角色」;alice是用户名,admin是角色名 - 不能写成
g, admin, user:delete—— 这是策略,不是角色继承关系 - 如果用
g2做多级继承(如g2, manager, admin),需额外声明[role_definition] g2 = _, _
权限校验失败时该返回什么 HTTP 状态码
Casbin 本身不处理响应,但实际项目中必须统一。RBAC 场景下,enforce() 返回 false 时,应明确区分两类情况:
- 用户未登录或 token 无效 → 返回
401 Unauthorized,由鉴权中间件兜底 - 用户已登录但无对应角色权限 → 返回
403 Forbidden,这是 RBAC 的语义本意 - 切忌所有失败都 throw new RuntimeException(),会导致 Swoole worker crash
最稳妥的做法是在 PermissionAspect 的 process 方法末尾加判断:
if (!$enforcer->enforce($role, $permission, $action)) {
throw new HttpException(403, 'Insufficient permissions');
}
复杂点在于:一个接口可能需要多个角色任一满足(如 admin|editor),这时不能只查一个角色,得遍历 getRolesByUserId() 返回的全部角色逐个 enforce,任一成功即放行。











