中间件是安全逻辑的执行载体,注册需确保config/middleware.php存在、返回带命名空间的类数组;顺序须按依赖关系排列,如ForceHttps前置;handle()中拦截逻辑必须在$next($request)前;JWT场景需显式注入用户态至$request属性。

中间件本身不是安全机制,而是安全逻辑的执行载体;没配对、没顺序、没兜底的中间件,比不写还危险。
中间件注册失败:类名、路径、数组结构三处必查
中间件不生效,90% 问题出在 config/middleware.php 文件。它必须存在、必须返回数组、数组每个元素必须是完整类名字符串(带命名空间),且不能有额外输出。
-
return [\app\middleware\AuthCheck::class];✅ 正确写法,注意反斜杠 -
return ['app\middleware\AuthCheck'];✅ 字符串形式也支持,但命名空间不能漏 -
return null;❌ 框架加载失败,静默跳过全部中间件 -
echo 'debug';在文件开头 ❌ PHP 解析中断,middleware.php加载失败 - 类文件放在
app/middleware/auth/AuthCheck.php❌ ThinkPHP 不扫描子目录,必须平铺在app/middleware/
执行顺序错乱:ForceHttps 和 ParseJsonBody 必须前置
安全中间件的顺序不是“谁重要放前面”,而是“谁依赖谁”。顺序错了,原始数据就读不到,鉴权就成空谈。
-
ForceHttpsMiddleware必须第一——它不依赖任何 request 属性,且需在所有解码前强制跳转 -
ValidatePostSize紧随其后——防大文件上传绕过后续校验 -
ParseJsonBody或VerifyCsrfToken必须在AuthMiddleware之前——否则$request->post()为空或 token 已被消费 -
AuthMiddleware和CheckPermission必须连续靠前——中间插个日志或语言包中间件,会导致权限判断晚于参数解析 -
Logger类中间件应靠后——它需要完整的$request->controller()和$request->action(),太早调用会拿不到
handle() 里 $next($request) 放错位置:拦截逻辑直接失效
中间件是否起效,不看注册顺序,而看 handle() 中 $next($request) 的调用时机。校验类中间件一旦把 return $next($request) 写在判断逻辑前面,等于开门揖盗。
立即学习“PHP免费学习笔记(深入)”;
- ❌ 错误写法:
$next($request); if (!$user) { return json(['code'=>401]); }—— 请求已进入控制器,拦截无效 - ✅ 正确写法:
if (!$user) { return json(['code'=>401]); } return $next($request);—— 拦截在最前 - 返回值必须是
think\Response实例:return redirect('/login')或return json(...),不能echo或die - 若需传参(如角色白名单),路由注册时写
->middleware(AuthCheck::class, 'admin'),handle()方法签名可扩展为handle($request, \Closure $next, $role)
JWT 场景下用户态丢失:Auth::id() 不可靠,必须显式注入
中间件默认拿不到登录态。Auth::id() 在 CLI、无 session 或 JWT 场景下极易为 null,硬读 session('user_id') 或 cookie 是常见错误。
- JWT 场景:前置中间件中必须用
$request->withAttr('user', $user)将用户信息写入请求属性,再通过$request->attr('user')['id']获取 - 兜底方案:读
X-User-IDHeader,但必须校验其与当前 token 的绑定关系,不可直接信任 - 动作用
$request->rule()->getRuleName()获取(如admin/user/delete),别用$request->url()或$request->pathinfo()解析——URL 可伪造,路由规则才真实 - 免审接口(如登录、登出、验证码)必须用
in_array($request->action(), ['login', 'logout', 'captcha'])过滤,别靠 URL 匹配,否则/api/v1/login?redirect=/admin就可能绕过
真正容易被忽略的是:所有校验类中间件都必须在 $next($request) 前完成全部判断,且不能依赖后续中间件初始化的数据;JWT 用户态必须由你亲手塞进 request 属性,框架不会自动帮你补全。



















