Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_NaiveVue/zh/appendix/middleware.md
T
2026-10-08 17:50:29 +08:00

137 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# F. 中间件执行顺序
:::tip 概述
本页展示请求在框架中的完整中间件执行流程,包括全局中间件和路由级中间件的执行顺序,帮助开发者理解请求处理链路。注册位置:`bootstrap/app.php`(别名 + API 组)与 `routes/api.php`(路由组)。
:::
## 执行流程图
```text
请求进入
│
▼
┌────────────────┐
│ProxyMiddleware │ API 组前置(最先执行)
│ │ 从代理头提取真实客户端 IP
└──────┬─────────┘
│
▼
┌──────────────┐
│ Cors │ API 组前置
│ │ 处理 OPTIONS 预检 + 跨域响应头
└──────┬───────┘
│
▼
┌──────────────┐
│ForceJson │ API 组前置
│Response │ 强制返回 JSON 响应头
└──────┬───────┘
│
▼
┌──────────────┐ 401
│Authenticate │──────────► 未认证(Token 缺失/无效/过期)
│ (auth.jwt) │ 403
│ JWT认证 │──────────► 无权限
│ 权限校验 │
└──────┬───────┘
│ 注入 userInfo(attributes)
▼
┌──────────────┐ 403
│ResolveTenant │──────────► 租户异常(禁用/过期)
│ (tenant) │
│ 租户上下文 │
└──────┬───────┘
│ 注入 tenantId / isSuperAdmin / tenantInfo
▼
┌──────────────┐ 403
│ demo │──────────► 演示模式下写操作被拦截
│ (demo 别名) │
│ 演示模式拦截 │
└──────┬───────┘
│ 放行:非演示模式 / GET / 有 #[DemoAllow]
▼
┌──────────────┐
│ LogOperation │ 记录操作日志(terminable,响应后写入)
│(log.operation)│ 写操作始终记录
└──────┬───────┘
│
▼
┌──────────────┐
│ 控制器方法 │
└──────────────┘
```
:::info 两种挂载方式
- **API 组前置**(`bootstrap/app.php` → `$middleware->api(prepend:)`):Proxy → Cors → ForceJson,对全部 `/api` 请求生效
- **路由组**(`routes/api.php` → `Route::middleware([...])->group(...)`):`auth.jwt` → `tenant` → `demo` → `log.operation`,仅认证接口组生效;登录、验证码、令牌刷新等公开接口定义在组外,天然免认证
:::
## 中间件职责
| 中间件 | 类型 | 职责 | 配置 |
|--------|------|------|------|
| ProxyMiddleware | API 组前置 | 代理头解析,提取真实客户端 IP | `.env` → `TRUSTED_PROXIES` |
| Cors | API 组前置 | 跨域处理(OPTIONS 预检 + 响应头) | `config/cors.php` → `CORS_ALLOW_ORIGIN` |
| ForceJsonResponse | API 组前置 | 强制 `Content-Type: application/json` | — |
| Authenticate | 路由组(别名 `auth.jwt`) | JWT 认证 + 权限校验(`#[Permission]` 注解) | `config/middleware.php` → `auth_except` |
| ResolveTenant | 路由组(别名 `tenant`) | 租户上下文注入与校验 | 超管(uid=1)跳过校验 |
| LogOperation | 路由组(别名 `log.operation`) | 操作日志记录(terminable,响应后落库) | `config/middleware.php` → `log_except` |
| DemoMiddleware | 路由组(别名 `demo`,tenant 与 log.operation 之间) | 演示模式拦截写操作(无 `#[DemoAllow]` 的写操作返回 403) | `config('app.demo_mode')` |
## 排除配置
```php
// config/middleware.php
return [
// Authenticate::handle() 命中时跳过 JWT 解析与权限校验(公开接口定义在路由组外,此为兜底)
'auth_except' => [
'login',
'captcha',
'refresh',
],
// LogOperation::handle() 命中时不写入 OperationLog
// 登录/登出由 LoginLog 单独记录
'log_except' => [
'login',
'logout',
'captcha',
],
];
```
## 注入内容
| 中间件 | 注入字段 | 读取方式 | 说明 |
|--------|---------|---------|------|
| ProxyMiddleware | `$_SERVER['REMOTE_ADDR']` | `request()->ip()` | 覆盖为真实客户端 IP |
| Authenticate | `userInfo` | `$request->attributes->get('userInfo')` | 解码后的 JWT 载荷(uid、username 等) |
| ResolveTenant | `tenantId` | `$request->attributes->get('tenantId')` | 租户 ID(0=平台用户) |
| ResolveTenant | `isSuperAdmin` | `$request->attributes->get('isSuperAdmin')` | 是否超级管理员(uid=1) |
| ResolveTenant | `tenantInfo` | `$request->attributes->get('tenantInfo')` | 非平台用户时注入租户模型实例 |
## ProxyMiddleware 详解
当项目部署在 CDN / 负载均衡 / Nginx 反向代理后面时,PHP 的 `REMOTE_ADDR` 是代理服务器的 IP(通常是 `127.0.0.1`),真实客户端 IP 由代理通过 `X-Forwarded-For` / `X-Real-IP` 请求头传递。
### 工作原理
```text
客户端(真实IP) → CDN/代理 → Nginx → PHP-FPM
↓ 设置头
X-Forwarded-For: 真实IP, 代理IP
X-Real-IP: 真实IP
```
1. 检查 `REMOTE_ADDR` 是否在受信代理列表(`.env` → `TRUSTED_PROXIES`,支持 CIDR 网段)
2. 如果是,从 `X-Real-IP` 或 `X-Forwarded-For` 头提取真实 IP
3. 覆盖 `$_SERVER['REMOTE_ADDR']`,后续 `request()->ip()` 与 `get_client_ip()` 返回真实 IP
### 配置
```ini
# .env
# 受信代理 IP(逗号分隔,支持 CIDR 网段)
# 未配置时默认信任 127.0.0.1 和 ::1
TRUSTED_PROXIES=127.0.0.1,::1,101.226.0.0/15,180.101.0.0/16
```