thinkphp rbac需手动实现,核心是rule_name严格匹配路由名(如admin/user/delete)、缓存key含role_id、权限动态存于auth_role_rule表。

ThinkPHP 本身不提供开箱即用的 RBAC 权限控制,必须手动建表、写中间件、配路由映射,否则 Auth::check() 只校验登录状态,拦不住任何接口请求。
auth_rule 表的 rule_name 必须和路由规则名严格对齐
权限拦截失效的最常见原因,是 rule_name 和实际路由不一致。TP6 的 request()->rule()->getName() 返回的是类似 admin/user/edit 这样的字符串,不是 admin.user.edit,也不是 user_edit。
常见错误现象:
- 菜单能显示,但点击「删除」按钮后端仍执行——因为按钮权限写成
user:delete,而路由规则名是admin/user/delete - 中间件里用
str_replace('/', '.', $name)转格式,结果缓存 key 和查库逻辑不统一,导致命中失败
实操建议:
- 直接用
request()->rule()->getName()的原始值作为权限节点标识,不加工、不拼接 - 路由定义必须显式绑定,比如
Route::post('admin/user/delete', 'UserController@delete'),不能靠隐式方法映射 -
auth_rule表中rule_name字段类型设为VARCHAR(100),加唯一索引,避免重复插入
中间件里别查库,要查缓存且 key 必须带 role_id
每次请求都 JOIN 三张表查权限,QPS 上百就扛不住。必须走缓存,但缓存设计错一步,权限就失控。
关键点:
- 缓存 key 必须包含
$roleId,例如auth_role_rules_{$roleId},不能只缓存全量all_rules - 缓存内容建议用 Redis Set 存储所有
rule_name字符串,用SISMEMBER判断快于 PHP 数组in_array - 权限变更时(如后台改角色权限),必须主动删掉对应
auth_role_rules_{$roleId},不能等过期
示例判断逻辑(中间件 handle 方法内):
if (!\think\facade\Cache::store('redis')->sismember('auth_role_rules_' . $roleId, $routeName)) {
throw new \think\Exception('Forbidden', 403);
}
Auth::setUser($uid) 必须在登录成功后立刻调用
很多人在登录控制器里跳转前才调用 Auth::setUser($uid),结果中间件一进来就 Auth::check() 失败——因为 TP6 的 Auth 类默认从 session 读缓存,而 session 里压根没写。
使用场景:
- 用户密码校验通过后,立即执行
\think\facade\Auth::setUser($uid) - 若用多应用(如
admin和api分开),需传第三个参数指定 app:Auth::setUser($uid, 'admin') - 别依赖
Auth::check('xxx')做路由拦截——它查的是 session 缓存,不是实时权限,且不支持 rule_name 映射
别把权限写进配置或常量,动态分配必须靠 auth_role_rule 中间表
权限一旦硬编码进 config/permission.php 或定义成 const,后面运营要临时关掉某个按钮,就得改代码、发版、清缓存——这不是动态,是伪动态。
实操底线:
-
auth_role_rule是唯一允许增删权限的地方,加一条记录 = 给角色加一个权限,删一条 = 收回权限 -
auth_rule表的status字段用于开关权限节点,不影响关联关系,适合灰度控制 - 用户表里不要存
permission_json字段,会破坏角色复用性;多角色支持走auth_role_access中间表,别用 JSON 字段模拟
容易被忽略的细节:权限变更后,前端菜单和按钮的显隐必须和服务端校验逻辑用同一套 rule_name,否则会出现「按钮点了没反应」或「403 但没提示」的情况。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











