权限验证中间件必须前置注册认证中间件并注入用户属性,用$request->rule()->getname()获取路由名,游客跳转、超管短路、权限缓存降级,ajax返回json、页面返回视图,且每个分支必须显式return。

权限验证中间件不是“注册了就能用”,它必须在正确时机、用正确方式获取用户和路由信息,否则 Auth::check() 返回 false、$request->rule()->getName() 报空、查权限总查不到——问题几乎都出在执行顺序和数据来源上。
中间件里拿不到用户?先看注册顺序和 attribute 注入
直接调 Auth::user() 或 $request->user() 为空,不是 Auth 没配置好,而是认证中间件还没执行。ThinkPHP 不自动把用户塞进请求对象,得靠前置中间件手动注入。
- 确保
app/middleware.php中,认证类(如CheckAuth)排在权限类(如CheckPermission)前面 - 认证中间件里必须显式调用
$request = $request->withAttribute('user', $user),后续中间件才能用$request->getAttribute('user')拿到 - 别在中间件
__construct()里查用户——此时$request还没绑定 session,Session::get()必然返回 null - 如果用 JWT,从
$request->header('Authorization')解析 token,别依赖 session 驱动
查不到路由名?别硬解析 URL,优先用 $request->rule()
$request->url() 是原始路径字符串,$request->route() 在中间件里常为 null,真正稳定可用的是 $request->rule()——它代表匹配成功的路由规则对象。
- 用
$request->rule()->getName()获取路由名(如'admin.user.delete'),这是 RBAC 权限表auth_rule.name字段的对应值 - 若路由没显式命名(比如没写
->name('admin.user.list')),getName()返回 null,必须补全:Route::get('user/list', 'User/list')->name('admin.user.list') - 不要用
$request->controller()+$request->action()拼接,大小写、驼峰转换、多应用前缀都会导致匹配失败 - 需要动态参数(如
/user/123中的123)?走$request->param('id'),不是$request->route()->param('id')(此时 route 尚未解析完成)
权限校验总失效?检查缓存、白名单和短路逻辑
返回 false 的 Auth::can() 大概率不是函数错了,而是权限节点没对齐、缓存没更新、或游客/超管没提前放行。
- 游客(未登录)必须第一时间跳转或返回 401,别让它进后续查询;超管(
is_super = 1)应最前判断并return $next($request),避免浪费一次 DB 查询 - 权限数据别每次查库:登录后把该用户所有
rule_name存进 Redis,键为user_permissions_{$uid},过期设 3600 秒;中间件里先Cache::get('user_permissions_'.$uid),空了再查库并回填 - 白名单不能只靠 if 判断路径,推荐给路由打标签:
Route::get('/login', 'Login@login')->middleware('auth:skip'),中间件内用$request->rule()->getOption('middleware')解析 - 缓存失效时要有降级:DB 挂了就 fallback 到
config/permissions.php,定义超管可访问全部接口,避免整个后台不可用
返回 403 页面还是 JSON?取决于请求头,不是写死
前后端分离项目里,中间件返回 redirect('/login') 对 AJAX 请求完全无效——前端收到 302 却拿不到 Location,控制台只显示空白响应。
- 用
$request->isAjax()或$request->header('accept') === 'application/json'判断是否为 API 请求 - AJAX 请求统一返回:
return json(['code' => 403, 'msg' => '无操作权限'], 403) - 页面请求可返回视图:
return view('error/403'),但要确保没启用think\middleware\JumpRedirect(它会把 redirect 自动转成跳转) - 别在中间件里调
$this->error()或$this->redirect()——这些是控制器方法,中间件无上下文会报错
最容易被忽略的其实是中间件的 return:漏掉 return $next($request) 分支,函数末尾隐式返回 null,框架就当成中断处理——表面看“拦住了”,实际连日志都打不出来,排查时得翻源码才明白为什么什么都没发生。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











