Files
group_xinghuo_jinrong/docs/course/jinrong-module-shared/modules/01-dual-auth.html
T

85 lines
5.7 KiB
HTML
Raw Normal View History

<section class="module" id="module-1">
<div class="module-inner">
<p class="eyebrow animate-in">模块 1 · 双栈鉴权</p>
<h1 class="module-title animate-in">JWT 双栈:<br>模块鉴权(deps.py)与宿主网关鉴权(gateway/auth_deps.py)各管一线</h1>
<p class="module-lead animate-in">
模块 API 走模块鉴权(<code>app/api/deps.py</code>)(<code>get_auth_context</code> / <code>get_platform_auth_context</code>);
宿主 Wave 0 网关走宿主网关鉴权(<code>app/gateway/auth_deps.py</code>)。
<strong>模块禁止 import gateway</strong>——指挥 AI 改鉴权时,先确认改的是哪条栈。
</p>
<div class="screen animate-in">
<h2>两套鉴权表(背下来少踩坑)</h2>
<table style="width:100%; border-collapse:collapse; margin: 1.5rem 0; font-size: 0.95rem;">
<thead><tr><th>通道</th><th>入口函数</th><th>X-Agent-Type</th><th>典型路由</th></tr></thead>
<tbody>
<tr><td>Agent / 对话</td><td><code>get_auth_context</code></td><td>JWT 通道<strong>必填</strong> + 准入矩阵</td><td>/api/chat, /api/risk/*, simulate</td></tr>
<tr><td>平台只读</td><td><code>get_platform_auth_context</code></td><td><strong>不要</strong></td><td>/api/customers/*, /api/analyst/*</td></tr>
<tr><td>宿主网关</td><td>宿主网关鉴权(<code>gateway/auth_deps.py</code>)· <code>get_auth_context</code></td><td>宿主口径</td><td>Wave 0 四件套(模块不 import)</td></tr>
</tbody>
</table>
<div class="callout callout-warning">
<strong>debug 头兜底:</strong> 仅 <code>APP_ENV=development</code> 且无 RS256 公钥时,可用 <code>X-Debug-Role</code> + <code>X-Debug-Actor</code> 冒充身份——pytest 和部分演示 SOP 依赖此通道;生产一律 Bearer JWT。
</div>
</div>
<div class="screen animate-in">
<div class="translation-block">
<div class="translation-code">
<span class="translation-label">模块鉴权(deps.py)· 对话线</span>
<pre><code><span class="code-line">agent_type = request.headers.get(<span class="code-string">"X-Agent-Type"</span>)</span>
<span class="code-line"><span class="code-keyword">if not</span> agent_type:</span>
<span class="code-line"> raise ApiError(<span class="code-number">401</span>, <span class="code-string">"AUTH_401_MISSING_AGENT_TYPE"</span>)</span>
<span class="code-line">assert_agent_access(auth, agent_type, ...)</span>
<span class="code-line"></span>
<span class="code-line"><span class="code-comment"># 平台线 — 无上述检查</span></span>
<span class="code-line"><span class="code-keyword">def</span> <span class="code-function">get_platform_auth_context</span>(request):</span>
<span class="code-line"> <span class="code-string">"""不要求 X-Agent-Type"""</span></span></code></pre>
</div>
<div class="translation-english">
<span class="translation-label">白话</span>
<div class="translation-lines">
<p class="tl">对话线:验完 JWT 还要读 Agent 头,并对照 AGENT_ACCESS_MATRIX。</p>
<p class="tl">例如客户 token 不能带 X-Agent-Type: risk,否则 403。</p>
<p class="tl">平台线:验 JWT 就放行到归属断言,不问你走哪条 Agent。</p>
<p class="tl">宿主网关鉴权(gateway/auth_deps.py)栈是宿主合并用,和模块鉴权(deps.py)双栈并存,别在模块里混 import。</p>
</div>
</div>
</div>
</div>
<div class="screen animate-in">
<h2>准入矩阵速览</h2>
<div class="badge-list">
<div class="badge-item"><span class="badge-code">customer</span><span class="badge-desc">仅 customer 角色</span></div>
<div class="badge-item"><span class="badge-code">advisor</span><span class="badge-desc">advisor / compliance / ops</span></div>
<div class="badge-item"><span class="badge-code">analyst</span><span class="badge-desc">analyst / compliance</span></div>
<div class="badge-item"><span class="badge-code">risk</span><span class="badge-desc">risk_officer / risk_manager / service_risk</span></div>
</div>
<div class="quiz-container" id="quiz-shared-m1">
<div class="quiz-question-block"
data-correct="option-b"
data-explanation-right="平台读 API 用 get_platform_auth_context,文件在模块鉴权(deps.py),与宿主网关鉴权(gateway/auth_deps.py)无关。"
data-explanation-wrong="宿主网关鉴权(gateway/auth_deps.py)是宿主 Wave 0;模块平台路由只 Depends 模块鉴权(deps.py)里的函数。">
<h3 class="quiz-question">customers.py 的 Depends 应该从哪 import?</h3>
<div class="quiz-options">
<button class="quiz-option" data-value="option-a" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>app.gateway.auth_deps</span>
</button>
<button class="quiz-option" data-value="option-b" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>app.api.deps.get_platform_auth_context</span>
</button>
<button class="quiz-option" data-value="option-c" onclick="selectOption(this)">
<div class="quiz-option-radio"></div><span>对话 HTTP 入口(chat.py)里的解析函数</span>
</button>
</div>
<div class="quiz-feedback"></div>
</div>
<button class="quiz-check-btn" onclick="checkQuiz('quiz-shared-m1')">检查答案</button>
<button class="quiz-reset-btn" onclick="resetQuiz('quiz-shared-m1')">重做</button>
</div>
</div>
</div>
</section>