首页 / 技术文章 / 框架与二次开发
框架与二次开发

ThinkPHP 6 的中间件鉴权,白名单该放哪

发布于 2026-11-12更新于 2026-11-12
ThinkPHP 6 的中间件鉴权,白名单该放哪

ThinkPHP 6 里把鉴权中间件挂成全局之后,健康检查、第三方回调这类开放接口常被一起拦下。这篇按挂载层级、白名单路径比对、401 与 403 的返回结构、校验后用户身份来源逐处拆开,末尾附一份改完能照着核对的清单。

健康检查接口回了 401

给 /api/health 加完探活之后,我用 curl -I 打了一次,返回 HTTP/1.1 401 Unauthorized。路由表里这个地址没有挂任何校验,控制器也只 return json(['ok' => true]),怎么看都不该走到鉴权分支。翻到 app/middleware.php 才明白,Auth 中间件挂在全局,请求先进它,健康检查也不例外。

白名单加一行确实能让它通。麻烦在于挂载层级和路由意图没对上,后面每加一个开放接口,都要回头改这个类一次。

我一般先把 app/middleware.php 和 route/api.php 并排打开,看一遍哪些分组本来就不该带业务 Token。

全局挂载和路由分组,改动范围不一样

ThinkPHP 6 的中间件入口是 handle($request, \Closure $next),请求进控制器之前会按注册顺序过一遍。全局中间件写在 app/middleware.php,多应用项目每个应用还能有自己的 middleware.php,路由级则用 ->middleware(Auth::class) 挂在分组上。三者能同时存在,执行顺序也固定,排查时我按这个顺序从上往下找,比在代码里全局搜类名快。挂在全局写起来省事,代价是每个请求都要在中间件里做一次字符串比对,路由一多,那段 if 就会越拉越长。路由分组的写法反过来,哪组要鉴权写在路由表里,没挂的那组本来就不校验,白名单几乎用不上。6.0 和 6.1 在多应用中间件加载顺序上有差别,更早版本的行为我没验证过,升级前后各跑一遍用例更稳妥。

我更倾向路由分组。

老项目往往已经有一整套全局中间件,硬迁到分组要动路由表、控制器和现有回归用例。这种情况我不太建议一次改完,先把白名单挪进配置,等哪次加接口时顺路把那一组拆出去。

白名单收在一份配置里

见过把路径写成类常量数组塞在 Auth.php 里的做法,加一个第三方回调地址就得发一次代码。挪到 config/auth.php 之后改的是配置,测试环境和生产环境也能各留一份,不用靠注释区分。

比对路径时有个坑,$request->pathinfo() 拿到的是去掉后缀与查询参数的部分,部署在子目录、或者带应用前缀的时候,和配置里写的字符串容易对不上。我会在中间件里把实际拿到的值打一行日志核一次再定写法,顺手用 strtolower() 和 rtrim() 统一大小写与结尾斜杠,别让 /api/health/ 变成漏网之鱼。OPTIONS 预检也算一类,跨域没在网关层处理干净,它会先撞上鉴权,前端看到的报错方向完全是错的。还有路径里带数字 ID 的情况,用精确字符串比对就得给每条路由写一条白名单,我会改成只比前缀,参数交给路由自己解析。这个要看项目里路由是不是规整,不规整就别硬套。

那份配置里只放路径,不放 Token 内容。

401 和 403 分开返回

别让框架把异常渲染成 HTML 错误页。前端按 JSON 解析,收到一整页标签只会报 parse error,排查时很容易往网络层找。我在中间件里直接 return json(),响应体和正常接口用同一套信封,code 用 401 表示没登录或 Token 失效,403 表示登录了但权限不够,HTTP 状态码也跟着分开返。

Token 过期和随手乱填一个 Token,我也尽量分开。客户端要靠这个决定跳登录页还是静默续期,合成一个 401 之后只能在前端再猜一次。日志里记失败原因、请求路径和 request id 够用了,别把完整 Token 写进去,凭据多存一份就多一处要清理的地方。

响应结构怎么定,可以顺着 RESTful 内容 API 设计 那套约定走,状态码和字段名全站共用一处定义。

校验通过之后身份从哪读

控制器还得知道当前是谁。老代码里常见 $request->user = $user 这样直接挂属性,PHP 8.2 起给对象加动态属性会报 deprecated,日志很快就被刷满。稳妥一点的做法是用容器绑定一个当前用户对象,或者在中间件里把 uid 明确塞进请求参数并约定来源,控制器只从这一处读。版本跨度大的项目可以先翻一遍 PHP 7.4 到 8.2 的兼容清单,看看还有没有别的写法要一起改。

有一条我会守着,用户身份只能来自服务端校验的结果。让前端顺手传一个 uid 上来、后端直接拿它查数据,等于把权限判断交给了请求参数,改个数字就能看到别人的记录。多应用项目还要留意容器绑定的作用域,别把后台的当前用户串到开放接口那边去;这块在我用过的结构里要单独确认一次,别的配置下能不能照搬不一定。那行 warning 可以先不管,等真正升级到 8.2 再一起收拾。

改完我按这几条过一遍

清单跑完再动生产配置,别在改路由的同时改白名单。

老项目接入中间件常要连着梳理原有鉴权范围和回归用例,这类活 PHPLEFT(西安尊云科技)承接程序定制开发、老系统二次开发与接口对接,可通过微信 yvsm316 说明需求。本文为技术经验分享,具体方案以实际沟通为准。

返回技术文章