在thinkphp 8.0中,通过注册authcheck中间件(置于路由匹配后)、使用$request->rule()->getname()获取路由名、区分游客/超管、调用authservice::check()校验权限,可实现不依赖第三方、不改核心、不写sql的rbac控制。

在 ThinkPHP 8.0 中快速搭建可运行的 RBAC 权限控制中间件链,要求不依赖第三方扩展、不修改核心 Auth 类、不手写 SQL 查询,且能准确拦截未授权请求并区分游客与超管。
注册中间件到 HTTP 链并确保执行时机正确
打开 app/middleware.php,找到 'http' => [] 数组,在靠后位置插入你的权限中间件类名,例如放在 SessionInit::class 和 AllowCrossDomain::class 之后,但不能放在已有鉴权中间件(如 CheckAuth::class)之后——否则会重复校验或错过路由解析结果。
这一步操作起来很简单,直接把字符串追加进数组就行:app\middleware\AuthCheck::class。
【中间件必须注册在路由匹配完成之后】。如果放太靠前,$request->rule()->getName() 会返回空或控制器反射路径(如 \app\controller\Admin\UserController@list),导致后续所有权限比对失效。
编写 AuthCheck 中间件类
在 app/middleware/AuthCheck.php 中创建类,继承 think\middleware\Middleware。
第一步:从请求对象中获取当前路由名,必须用 $request->rule()->getName(),不要用 $request->url() 或拼接 $request->controller() . '.' . $request->action()——大小写和命名风格极易出错,且无法兼容资源路由自动注册的七种动作(如 article/create)。
第二步:判断是否为游客(未登录)。检查 session('user_id') 是否存在,不存在则重定向到登录页:return redirect('/login')->with('msg', '请先登录');。
第三步:判断是否为超管。查用户表字段 is_super = 1,满足则直接 return $next($request) 放行,跳过全部权限查询。
第四步:非游客非超管时,调用自定义权限服务进行校验。不要在中间件里硬编码查询逻辑,应封装成 app\service\AuthService::check($routeName, $uid) 方法,内部走「用户→角色→权限」三级链路,最终返回布尔值。
实现 AuthService 权限校验服务
方法一:使用 ThinkPHP 自带 Auth 类(需提前配置好)
确保登录成功后已执行 Auth::setUser($uid),这是权限缓存生效的前提;否则 Auth::check() 永远返回 false。
在 AuthService::check() 中调用:Auth::check($routeName, $uid, 'admin') ——注意第三个参数传应用名,多应用模式下不传会默认查 app 应用下的规则,导致权限名匹配失败。
方法二:手动查四表关联(更可控,推荐)
① 根据 $uid 查 auth_group_access 表得 role_id 列表;
② 用这些 role_id 查 auth_role_rule 得 rule_id 集合;
③ 联查 auth_rule 表,提取 rule_name 字符串数组(如 ['admin.user.list', 'admin.user.delete']);
④ 用 in_array($routeName, $rules) 判断是否命中。
【权限名必须与路由定义完全一致】。比如路由定义是 Route::get('admin/user/list', 'admin.UserController@list')->name('admin.user.list');,那对应权限名只能是 admin.user.list,不能是 admin/user/list 或 user_list。
确保路由显式命名且权限表数据对齐
打开 route/app.php,所有需要权限控制的路由必须显式调用 ->name('xxx')。没有 name 的路由,$request->rule()->getName() 就拿不到有效值。
往 auth_rule 表插入数据时,name 字段必须与上述 ->name() 参数完全一致,包括大小写、点号分隔、无多余斜杠。
别把权限写成 user:edit 或 /admin/user/edit,ThinkPHP RBAC 不识别这种格式。
插入示例:INSERT INTO `auth_rule` (`name`, `title`, `type`) VALUES ('admin.user.list', '用户列表页', 1);











