Files
rxthinkcmf-wiki/RXThinkCMF_Laravel13_NaiveVue/.vitepress/dist/assets/zh_appendix_middleware.md.DaJw1rqi.lean.js
T
2026-10-11 19:06:33 +08:00

59 lines
12 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
import{_ as s,o as n,c as e,a2 as t}from"./chunks/framework.BbmSSMjL.js";const u=JSON.parse('{"title":"F. 中间件执行顺序","description":"","frontmatter":{},"headers":[],"relativePath":"zh/appendix/middleware.md","filePath":"zh/appendix/middleware.md"}'),d={name:"zh/appendix/middleware.md"};function p(o,a,l,i,c,r){return n(),e("div",null,[...a[0]||(a[0]=[t(`<h1 id="F-中间件执行顺序" tabindex="-1">F. 中间件执行顺序 <a class="header-anchor" href="#F-中间件执行顺序" aria-label="Permalink to &quot;F. 中间件执行顺序&quot;">​</a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>本页展示请求在框架中的完整中间件执行流程,包括全局中间件和路由级中间件的执行顺序,帮助开发者理解请求处理链路。注册位置:<code>bootstrap/app.php</code>(别名 + API 组)与 <code>routes/api.php</code>(路由组)。</p></div><h2 id="执行流程图" tabindex="-1">执行流程图 <a class="header-anchor" href="#执行流程图" aria-label="Permalink to &quot;执行流程图&quot;">​</a></h2><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>请求进入</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌────────────────┐</span></span>
<span class="line"><span>│ProxyMiddleware │ API 组前置(最先执行)</span></span>
<span class="line"><span>│ │ 从代理头提取真实客户端 IP</span></span>
<span class="line"><span>└──────┬─────────┘</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌──────────────┐</span></span>
<span class="line"><span>│ Cors │ API 组前置</span></span>
<span class="line"><span>│ │ 处理 OPTIONS 预检 + 跨域响应头</span></span>
<span class="line"><span>└──────┬───────┘</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌──────────────┐</span></span>
<span class="line"><span>│ForceJson │ API 组前置</span></span>
<span class="line"><span>│Response │ 强制返回 JSON 响应头</span></span>
<span class="line"><span>└──────┬───────┘</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌──────────────┐ 401</span></span>
<span class="line"><span>│Authenticate │──────────► 未认证(Token 缺失/无效/过期)</span></span>
<span class="line"><span>│ (auth.jwt) │ 403</span></span>
<span class="line"><span>│ JWT认证 │──────────► 无权限</span></span>
<span class="line"><span>│ 权限校验 │</span></span>
<span class="line"><span>└──────┬───────┘</span></span>
<span class="line"><span> │ 注入 userInfo(attributes)</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌──────────────┐ 403</span></span>
<span class="line"><span>│ResolveTenant │──────────► 租户异常(禁用/过期)</span></span>
<span class="line"><span>│ (tenant) │</span></span>
<span class="line"><span>│ 租户上下文 │</span></span>
<span class="line"><span>└──────┬───────┘</span></span>
<span class="line"><span> │ 注入 tenantId / isSuperAdmin / tenantInfo</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌──────────────┐ 403</span></span>
<span class="line"><span>│ demo │──────────► 演示模式下写操作被拦截</span></span>
<span class="line"><span>│ (demo 别名) │</span></span>
<span class="line"><span>│ 演示模式拦截 │</span></span>
<span class="line"><span>└──────┬───────┘</span></span>
<span class="line"><span> │ 放行:非演示模式 / GET / 有 #[DemoAllow]</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌──────────────┐</span></span>
<span class="line"><span>│ LogOperation │ 记录操作日志(terminable,响应后写入)</span></span>
<span class="line"><span>│(log.operation)│ 写操作始终记录(#[WithoutOperationLog] 跳过)</span></span>
<span class="line"><span>└──────┬───────┘</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌──────────────┐</span></span>
<span class="line"><span>│ 控制器方法 │</span></span>
<span class="line"><span>└──────────────┘</span></span></code></pre></div><div class="info custom-block"><p class="custom-block-title">两种挂载方式</p><ul><li><strong>API 组前置</strong>(<code>bootstrap/app.php</code> → <code>$middleware-&gt;api(prepend:)</code>):Proxy → Cors → ForceJson,对全部 <code>/api</code> 请求生效</li><li><strong>路由组</strong>(<code>routes/api.php</code> → <code>Route::middleware([...])-&gt;group(...)</code>):<code>auth.jwt</code> → <code>tenant</code> → <code>demo</code> → <code>log.operation</code>,仅认证接口组生效;登录、验证码、令牌刷新等公开接口定义在组外,天然免认证</li></ul></div><h2 id="中间件职责" tabindex="-1">中间件职责 <a class="header-anchor" href="#中间件职责" aria-label="Permalink to &quot;中间件职责&quot;">​</a></h2><table tabindex="0"><thead><tr><th>中间件</th><th>类型</th><th>职责</th><th>配置</th></tr></thead><tbody><tr><td>ProxyMiddleware</td><td>API 组前置</td><td>代理头解析,提取真实客户端 IP</td><td><code>.env</code> → <code>TRUSTED_PROXIES</code></td></tr><tr><td>Cors</td><td>API 组前置</td><td>跨域处理(OPTIONS 预检 + 响应头)</td><td><code>config/cors.php</code> → <code>CORS_ALLOW_ORIGIN</code></td></tr><tr><td>ForceJsonResponse</td><td>API 组前置</td><td>强制 <code>Content-Type: application/json</code></td><td>—</td></tr><tr><td>Authenticate</td><td>路由组(别名 <code>auth.jwt</code>)</td><td>JWT 认证 + 权限校验(<code>#[Permission]</code> 注解)</td><td><code>config/jwt.php</code> → <code>header_prefix</code></td></tr><tr><td>ResolveTenant</td><td>路由组(别名 <code>tenant</code>)</td><td>租户上下文注入与校验</td><td>超管(uid=1)跳过校验</td></tr><tr><td>LogOperation</td><td>路由组(别名 <code>log.operation</code>)</td><td>操作日志记录(terminable,响应后落库,<code>#[WithoutOperationLog]</code> 跳过)</td><td>—</td></tr><tr><td>DemoMiddleware</td><td>路由组(别名 <code>demo</code>,tenant 与 log.operation 之间)</td><td>演示模式拦截写操作(无 <code>#[DemoAllow]</code> 的写操作返回 403)</td><td><code>config(&#39;app.demo_mode&#39;)</code></td></tr></tbody></table><h2 id="排除方式" tabindex="-1">排除方式 <a class="header-anchor" href="#排除方式" aria-label="Permalink to &quot;排除方式&quot;">​</a></h2><table tabindex="0"><thead><tr><th>目标</th><th>机制</th><th>说明</th></tr></thead><tbody><tr><td>认证(<code>auth.jwt</code>)</td><td>路由分组</td><td>公开接口(<code>login</code>、<code>captcha</code>、<code>refresh</code>、<code>oauth2</code>)定义在认证组外,天然免认证</td></tr><tr><td>操作日志(<code>log.operation</code>)</td><td><code>#[WithoutOperationLog]</code> 注解</td><td>方法标注即不写入 <code>OperationLog</code>,优先级高于&quot;写操作一律记录&quot;;适用于已有专门日志载体的接口,如登出记录写入登录日志(<code>login_log</code>,<code>log_type=2</code>)</td></tr></tbody></table><p>操作日志排除机制详见 <a href="/zh/architecture/request-flow.html">4.2 请求处理链路</a>。</p><h2 id="注入内容" tabindex="-1">注入内容 <a class="header-anchor" href="#注入内容" aria-label="Permalink to &quot;注入内容&quot;">​</a></h2><table tabindex="0"><thead><tr><th>中间件</th><th>注入字段</th><th>读取方式</th><th>说明</th></tr></thead><tbody><tr><td>ProxyMiddleware</td><td><code>$_SERVER[&#39;REMOTE_ADDR&#39;]</code></td><td><code>request()-&gt;ip()</code></td><td>覆盖为真实客户端 IP</td></tr><tr><td>Authenticate</td><td><code>userInfo</code></td><td><code>$request-&gt;attributes-&gt;get(&#39;userInfo&#39;)</code></td><td>解码后的 JWT 载荷(uid、username 等)</td></tr><tr><td>ResolveTenant</td><td><code>tenantId</code></td><td><code>$request-&gt;attributes-&gt;get(&#39;tenantId&#39;)</code></td><td>租户 ID(0=平台用户)</td></tr><tr><td>ResolveTenant</td><td><code>isSuperAdmin</code></td><td><code>$request-&gt;attributes-&gt;get(&#39;isSuperAdmin&#39;)</code></td><td>是否超级管理员(uid=1)</td></tr><tr><td>ResolveTenant</td><td><code>tenantInfo</code></td><td><code>$request-&gt;attributes-&gt;get(&#39;tenantInfo&#39;)</code></td><td>非平台用户时注入租户模型实例</td></tr></tbody></table><h2 id="ProxyMiddleware-详解" tabindex="-1">ProxyMiddleware 详解 <a class="header-anchor" href="#ProxyMiddleware-详解" aria-label="Permalink to &quot;ProxyMiddleware 详解&quot;">​</a></h2><p>当项目部署在 CDN / 负载均衡 / Nginx 反向代理后面时,PHP 的 <code>REMOTE_ADDR</code> 是代理服务器的 IP(通常是 <code>127.0.0.1</code>),真实客户端 IP 由代理通过 <code>X-Forwarded-For</code> / <code>X-Real-IP</code> 请求头传递。</p><h3 id="工作原理" tabindex="-1">工作原理 <a class="header-anchor" href="#工作原理" aria-label="Permalink to &quot;工作原理&quot;">​</a></h3><div class="language-text vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">text</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span>客户端(真实IP) → CDN/代理 → Nginx → PHP-FPM</span></span>
<span class="line"><span> ↓ 设置头</span></span>
<span class="line"><span> X-Forwarded-For: 真实IP, 代理IP</span></span>
<span class="line"><span> X-Real-IP: 真实IP</span></span></code></pre></div><ol><li>检查 <code>REMOTE_ADDR</code> 是否在受信代理列表(<code>.env</code> → <code>TRUSTED_PROXIES</code>,支持 CIDR 网段)</li><li>如果是,从 <code>X-Real-IP</code> 或 <code>X-Forwarded-For</code> 头提取真实 IP</li><li>覆盖 <code>$_SERVER[&#39;REMOTE_ADDR&#39;]</code>,后续 <code>request()-&gt;ip()</code> 与 <code>get_client_ip()</code> 返回真实 IP</li></ol><h3 id="配置" tabindex="-1">配置 <a class="header-anchor" href="#配置" aria-label="Permalink to &quot;配置&quot;">​</a></h3><div class="language-ini vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">ini</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># .env</span></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># 受信代理 IP(逗号分隔,支持 CIDR 网段)</span></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># 未配置时默认信任 127.0.0.1 和 ::1</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">TRUSTED_PROXIES</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">127.0.0.1,::1,101.226.0.0/15,180.101.0.0/16</span></span></code></pre></div>`,19)])])}const g=s(d,[["render",p]]);export{u as __pageData,g as default};