### [Laravel中间件实现接口统一鉴权与请求过滤的最佳实践](https://www.huociguo.com/article/149) **Published:** 2026-07-15T05:59:56 **Author:** 米了 **Excerpt:** Laravel 中间件(Middleware)是 HTTP 请求进入应用前的统一拦截层,在 API 场景下同时承担身份认证与请求过滤两项职责,可避免将鉴权逻辑散落在各控制器中。下文基于 Laravel 12(当前稳定版,安全支持至 2027 Laravel 中间件(Middleware)是 HTTP 请求进入应用前的统一拦截层,在 API 场景下同时承担**身份认证**与**请求过滤**两项职责,可避免将鉴权逻辑散落在各控制器中。下文基于 Laravel 12(当前稳定版,安全支持至 2027 年 2 月)与 Sanctum 方案展开,所有代码示例均可直接落地 。 ## 一、中间件分层设计思路 Laravel 的 `routes/api.php`默认挂载 `api`中间件组(含 `throttle:api`、`bindings`),这一层作为**API 基座**提供限流与路由模型绑定;在此之上叠加 `auth:sanctum`作为**身份门禁**,形成”基座 + 门禁”的双层结构 。 设计原则: 1. **公开接口**(登录、注册、验证码)仅挂 `api`,享受限流但跳过认证 2. **私有接口**按版本分组后统一挂 `auth:sanctum`,避免逐个控制器声明 3. **版本前缀**​ `Route::prefix('v1')`必选,为后续迭代留回退路径 > 💡 若项目涉及第三方开发者接入,才考虑 Passport(OAuth2);90% 的第一方 SPA / 移动端场景,Sanctum 更轻量 。 ## 二、统一鉴权中间件实现 ### 1\. 创建专用中间件 不建议直接复用框架自带的 `auth`别名,而是封装一层业务级中间件,便于后续追加审计逻辑: ```bash php artisan make:middleware AuthenticateApi ``` ```php user($guard)) { return response()->json([ 'code' => 401, 'message' => '未授权,请先登录', 'data' => null, ], 401); } // 2. 可选:Token 行为审计(同一 token 短时高频调用) $this->auditTokenUsage($request->user($guard)); return $next($request); } private function auditTokenUsage($user): void { $key = 'token_hit:' . ($user->currentAccessToken()->id ?? $user->id); $count = cache()->increment($key); cache()->put($key, $count, 60); if ($count > 300) { // 单 Token 每分钟 300 次阈值,按需调整 abort(429, 'Token 调用频次异常'); } } } ``` 注册到 `app/Http/Kernel.php`的 `$routeMiddleware`: ```php protected $routeMiddleware = [ // ... 'auth.api' => \App\Http\Middleware\AuthenticateApi::class, ]; ``` ### 2\. 路由层收口 ```php // routes/api.php Route::prefix('v1')->group(function () { // 公开 Route::post('login', [AuthController::class, 'login'])->middleware('throttle:10,1'); // 私有,统一鉴权 Route::middleware(['auth.api'])->group(function () { Route::apiResource('orders', OrderController::class); Route::apiResource('users', UserController::class)->shallow(); }); }); ``` ### 3\. 多 Guard 切换 当项目同时存在 SPA(session)、移动端(sanctum token)、微服务调用(自定义 guard)时,可在中间件参数中透传 guard: ```php Route::middleware('auth.api:sanctum')->group(...); // 移动端 Route::middleware('auth.api:web')->group(...); // SPA ``` ## 三、请求过滤层的实现 鉴权之后、业务之前,还需一道**入口级过滤**——注意区分”中间件过滤”与”FormRequest 校验”的边界: | | | | | --- | --- | --- | | 层级 | 职责 | 典型手段 | | 中间件 | 入口级、无业务语义 | UA 校验、IP 白名单、签名、防重放 nonce、限流 | | FormRequest | 业务参数级 | 字段类型、枚举、存在性、关联归属 | ### 1\. 中间件执行顺序(关键) Kernel 中 `$middlewarePriority`决定同路由多中间件的执行先后,**auth 应置于日志、CORS 之后,业务中间件之前**,防止未认证请求仍触发日志污染或 CORS 预检异常 。 ``` CORS → 日志 → 限流 → auth → 业务中间件 → 控制器 ``` ### 2\. 请求过滤中间件示例(防刷 + UA 基础校验) ```php header('User-Agent', ''); if ($ua === '' || preg_match('/^(curl|python|scrapy)/i', $ua)) { abort(403, '非法请求来源'); } // 强制 API 必须带自定义标识头(可选) if (! $request->hasHeader('X-Client-Version')) { abort(400, '缺失客户端版本标识'); } // 滑动窗口用户级计数(基于 token 或 user_id) $key = 'req:' . ($request->user()?->id ?? $request->ip()); $window = cache()->remember($key, 60, fn () => 0); if ($window > 1000) { abort(429); } cache()->increment($key); return $next($request); } } ``` > ⚠️ 中间件内禁止做耗时的 DB 查询或远程调用,所有判断应缓存友好、无副作用 。 ### 3\. 与限流中间件协同 Laravel 内置 `throttle`已支持 Redis 后端,生产环境建议将 `cache`驱动切到 Redis,避免多实例计数不一致: ```php Route::middleware(['throttle:api', 'auth.api', 'filter.request']) ->group(...); ``` ## 四、完整链路图示 下图展示一个请求从进入 Laravel 到抵达控制器的中间件执行顺序与职责划分: ![](https://api.huociguo.com/wp-content/uploads/2026/07/13428568611435395-15052a2104-1.png "13428568611435395-15052a2104-1") (图中 `api`组提供基座,`auth.api`做统一鉴权,`filter.request`做入口过滤,最后进入业务控制器;失败可在任一环节提前 401/429 短路返回。) ## 常见与排查 - `auth:api`**与** `auth:sanctum`**混淆**:使用 Sanctum 必须写 `auth:sanctum`,写 `auth:api`会走默认 driver,导致 token 解析失败 - `authorize()`**里写限流**:FormRequest 的 `authorize()`失败返 403 而非 429,语义错误,限流应放中间件 - **SPA 跨域场景**:Sanctum SPA 模式依赖 session + CSRF cookie,需先调 `/sanctum/csrf-cookie`再登录,前端 `credentials: include`必带 - **路由缓存后中间件不生效**:`php artisan route:clear`后再测,缓存状态下新增中间件别名不会自动加载 **Tags:** API鉴权, Laravel, PHP, 中间件, 请求过滤 **Categories:** PHP教程 ---