ThinkPHP 8.0中间件权限控制核心是在请求进入控制器前完成校验,需通过路由绑定中间件、显式携带权限标识(如option或命名路由)、中间件内基于缓存校验权限码三者配合,禁用控制器内if判断和路由元数据自动鉴权。

ThinkPHP 8.0 的中间件权限控制,核心是“在请求进入控制器前完成校验”,不是靠路由元数据自动触发,也不是在控制器里写 if 判断——必须通过中间件拦截 + 显式权限标识 + 可靠校验逻辑三者配合。
路由层绑定权限中间件
权限控制要精准到具体操作,不能全局一刀切。推荐按路由分组或单个路由绑定中间件:
- 对后台模块统一加权限中间件:
Route::group('admin', function () {
Route::get('users', 'admin/User@index');
Route::post('roles', 'admin/Role@save');
})->middleware('check_permission'); - 对关键接口单独控制:
Route::delete('article/{id}', 'ArticleController@destroy')->middleware('check_permission'); - 资源路由需显式指定,不能依赖自动继承:
Route::resource('user', 'UserController')->only(['index', 'store'])->middleware('check_permission');
中间件内获取并校验权限标识
中间件是执行权限判断的实际位置,必须从当前请求中提取出可比对的权限码(如 article:delete),再调用你的权限服务:
- 优先用命名路由 + option() 携带权限码:
Route::post('article/publish', 'ArticleController@publish')->name('article.publish')->option(['permission' => 'article:publish']);
中间件中读取:
$permission = $request->route()?->getOption('permission'); - 若未显式定义,可按约定解析路由名或控制器方法:
$name = $request->route()?->getName(); // 如 'admin.article.edit'
或
$action = $request->route()?->getActionName(); // 如 'admin/Article@edit'
再转换为标准权限码(如 article:edit) - 校验时别直连数据库查表,应走缓存(如 Redis 中按角色预存权限集合),用 SISMEMBER 实现 O(1) 判断
注意中间件的返回与执行顺序
中间件不生效,90% 是因为返回值或注册位置不对:
立即学习“PHP免费学习笔记(深入)”;
- 每个分支都必须显式 return:
校验通过 → return $next($request);
不通过 → return abort(403) 或 return json(['code'=>403]);
漏掉 return $next($request) 会导致隐式返回 null,框架直接中断请求 - 权限中间件应放在认证中间件之后(确保能拿到用户 ID),但早于日志、性能监控等中间件
- 全局中间件(app/middleware.php)只适合通用逻辑,权限控制务必走路由绑定,否则无法区分不同接口的权限粒度
API 场景要适配无状态鉴权
如果是前后端分离项目,别用依赖 Session 的 Auth::check():
- JWT 场景:认证中间件解析 token 后,把用户 ID、角色、权限列表等注入 request 属性(如 $request->withAttribute('auth_user', $payload)),权限中间件从中读取
- 权限校验逻辑必须基于 payload 中的字段或 Redis 缓存,不能查 Session 或调用含 session 依赖的 Auth 方法
- 按钮显隐、菜单过滤只是前端体验,后端接口必须重复校验,且响应数据中敏感字段(如手机号、余额)要按权限做字段级脱敏



















