ThinkPHP中间件需按认证→权限顺序注册,避免构造函数中获取用户;统一用$request->getAttribute('user')取用户;权限校验应基于URL路径匹配白名单或路由信息;权限数据须缓存并设降级策略;响应格式需依Accept头动态返回JSON或403页面。

中间件里拿不到用户信息?检查 $request 和 Auth 初始化时机
ThinkPHP 中间件默认不自动加载用户认证逻辑,直接调用 Auth::check() 或读取 $request->user() 很可能返回空或报错。这不是权限逻辑写错了,而是执行顺序问题:认证中间件必须在权限校验中间件之前注册。
- 确保
app/middleware.php中,认证类(如CheckAuth)排在权限类(如CheckPermission)前面 - 不要在中间件构造函数里尝试获取用户,
__construct()阶段$request尚未完成绑定,$request->session()可能不可用 - 统一从
$request->getAttribute('user')取用户对象——前提是前置认证中间件已通过$request = $request->withAttribute('user', $user)注入
handle() 里怎么判断当前路由是否需要权限?用 $request->url() + 配置白名单
硬编码每个控制器方法的权限太脆弱,也违背中间件“横切”的本意。更可靠的方式是按 URL 路径匹配预设规则,再查数据库或配置项做映射。
- 把免登录路径(如
/api/login、/public/*)写进配置数组,用str_starts_with($path, $prefix)快速放行 - 对需鉴权路径,提取控制器/方法名:
$route = $request->route()->getName() ?: $request->url()->getPath(),避免依赖路由别名不稳定 - 注意
$request->route()在未匹配到路由时会返回null,要先判空,否则调用getName()报Call to a member function getName() on null
数据库查权限太慢?缓存策略和兜底逻辑必须一起上
每次请求都查一次 role_permission 表,QPS 上去后 DB 直接成瓶颈。但全靠 Redis 缓存又怕权限变更不及时,得有折中方案。
- 用
Cache::remember("permission_{$roleId}", 3600, fn() => Db::name('role_permission')->where('role_id', $roleId)->column('rule'))缓存角色权限列表,过期时间设为 1 小时足够平衡 - 缓存失效时,降级走本地配置文件(如
config/permissions.php),定义超级管理员可访问所有接口,避免 DB 挂了整个系统 403 - 别用
Cache::get('xxx') === null判断缓存是否存在——ThinkPHP 的 File 缓存驱动对空值也存,要用Cache::has('xxx')
返回 403 页面还是 JSON?根据 $request->isAjax() 和 Accept 头动态响应
同一个中间件既要处理 Vue 的 fetch 请求,又要兼容浏览器直接访问后台页面,强行统一返回格式会导致前端解析失败或页面空白。
- 优先看请求头:
$request->header('Accept')包含application/json就返回 JSON 错误,否则跳转到 403 模板页 - 别只依赖
$request->isAjax(),某些 POST 表单提交也带X-Requested-With: XMLHttpRequest,但后端期望的是重定向 - JSON 响应务必设置状态码:
return json(['code' => 403, 'msg' => '无访问权限'], 403),否则前端response.status还是 200
权限中间件最麻烦的从来不是怎么写,而是怎么让「没权限」这件事既不暴露后端结构,又能让前端明确知道该做什么。缓存键设计、响应格式分支、以及前置中间件的依赖顺序,这三个点漏掉任何一个,线上就容易出静默失败。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










