Files
rxthinkcmf-wiki/RXThinkCMF_ThinkPhp8_EleVue/.vitepress/dist/assets/zh_api-spec_token.md.jMkJ5xgQ.js
T
2026-10-25 18:52:36 +08:00

299 lines
90 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 i,o as a,c as n,a2 as t}from"./chunks/framework.B-QBKwc1.js";const F=JSON.parse('{"title":"Token 使用说明","description":"系统采用 JWT 双令牌认证,access_token 用于接口认证(2小时),refresh_token 用于刷新令牌(7天)。","frontmatter":{},"headers":[],"relativePath":"zh/api-spec/token.md","filePath":"zh/api-spec/token.md"}'),p={name:"zh/api-spec/token.md"};function h(l,s,e,k,d,r){return a(),n("div",null,[...s[0]||(s[0]=[t(`<h1 id="_8-6-Token-使用说明" tabindex="-1">8.6 Token 使用说明 <a class="header-anchor" href="#_8-6-Token-使用说明" aria-label="Permalink to &quot;8.6 Token 使用说明&quot;">​</a></h1><div class="tip custom-block"><p class="custom-block-title">概述</p><p>系统采用 JWT 双令牌认证,access_token 用于接口认证(2小时),refresh_token 用于刷新令牌(7天)。所有需认证的接口必须在请求头中携带 access_token。</p></div><h2 id="Token-完整生命周期" tabindex="-1">Token 完整生命周期 <a class="header-anchor" href="#Token-完整生命周期" aria-label="Permalink to &quot;Token 完整生命周期&quot;">​</a></h2><h3 id="登录获取-Token" tabindex="-1">登录获取 Token <a class="header-anchor" href="#登录获取-Token" aria-label="Permalink to &quot;登录获取 Token&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>用户输入账号密码</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>┌─────────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│ 前端:POST /api/login │</span></span>
<span class="line"><span>│ { username, password, code, key } │</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>│ 后端:LoginController → LoginLogic → JwtService │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ 1. CaptchaService::check() 验证码校验 │</span></span>
<span class="line"><span>│ 2. User::where(&#39;username&#39;) 查询用户 │</span></span>
<span class="line"><span>│ 3. PasswordService::verify() 密码校验 │</span></span>
<span class="line"><span>│ 4. Jwt::createAccessToken() 签发 access_token(2小时) │</span></span>
<span class="line"><span>│ Jwt::createRefreshToken() 签发 refresh_token(7天) │</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>
<span class="line"><span>│ &quot;accessToken&quot;: &quot;eyJ...&quot;, ← 用于接口认证 │</span></span>
<span class="line"><span>│ &quot;refreshToken&quot;: &quot;eyJ...&quot;, ← 用于刷新令牌 │</span></span>
<span class="line"><span>│ &quot;tokenType&quot;: &quot;Bearer&quot;, │</span></span>
<span class="line"><span>│ &quot;expiresIn&quot;: 7200, ← 2小时 = 7200秒 │</span></span>
<span class="line"><span>│ &quot;user&quot;: { &quot;id&quot;:1, &quot;username&quot;:&quot;admin&quot; } │</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>│ 前端:存储 Token │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ localStorage.setItem(&#39;access_token&#39;, accessToken) │</span></span>
<span class="line"><span>│ localStorage.setItem(&#39;refresh_token&#39;, refreshToken) │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────────┘</span></span></code></pre></div><h3 id="使用-Token-请求-API" tabindex="-1">使用 Token 请求 API <a class="header-anchor" href="#使用-Token-请求-API" aria-label="Permalink to &quot;使用 Token 请求 API&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>用户操作(如点击&quot;用户列表&quot;)</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>│ const token = localStorage.getItem(&#39;access_token&#39;) │</span></span>
<span class="line"><span>│ config.headers[&#39;Authorization&#39;] = \`Bearer \${token}\` │</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>│ GET /api/user/page?pageNo=1&amp;pageSize=10 │</span></span>
<span class="line"><span>│ Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 │</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>│ 后端:AuthMiddleware │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ 1. Jwt::getTokenFromHeader() 从请求头提取 Token │</span></span>
<span class="line"><span>│ │ 空 → 返回 401 &quot;未提供认证令牌&quot; │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ 2. Jwt::decode($token) 解码并校验签名+有效期 │</span></span>
<span class="line"><span>│ │ 过期 → 返回 401 &quot;token已过期&quot; │</span></span>
<span class="line"><span>│ │ 签名无效 → 返回 401 &quot;token签名无效&quot; │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ 3. $decoded-&gt;type === &#39;access&#39; 校验 Token 类型 │</span></span>
<span class="line"><span>│ │ 非 access → 返回 401 &quot;无效的认证令牌&quot; │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ 4. $request-&gt;userInfo = $decoded 注入用户信息 │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ 5. checkPermission() 权限校验 │</span></span>
<span class="line"><span>│ │ 无权限 → 返回 403 &quot;无访问权限&quot; │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ 6. 放行 → Controller → Logic → Model → 响应 │</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>
<span class="line"><span>│ code === 0 → 返回 data(成功) │</span></span>
<span class="line"><span>│ code === 401 → Token 过期,尝试刷新 │</span></span>
<span class="line"><span>│ code === 403 → 提示&quot;无访问权限&quot; │</span></span>
<span class="line"><span>│ 其他 → 提示 msg │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────────┘</span></span></code></pre></div><h3 id="Token-过期自动刷新" tabindex="-1">Token 过期自动刷新 <a class="header-anchor" href="#Token-过期自动刷新" aria-label="Permalink to &quot;Token 过期自动刷新&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>前端请求 API → 后端返回 code=401 &quot;token已过期&quot;</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>│ │</span></span>
<span class="line"><span>│ if (code === 401 &amp;&amp; !config._retry) { │</span></span>
<span class="line"><span>│ // 尝试用 refresh_token 刷新 │</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>│ 前端:POST /api/oauth2/token │</span></span>
<span class="line"><span>│ { grant_type: &quot;refresh_token&quot;, refresh_token: &quot;eyJ...&quot; } │</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>│ 后端:JwtService::refreshToken() │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ 1. Jwt::decode(refreshToken) 解码 refresh_token │</span></span>
<span class="line"><span>│ │ 无效/过期 → 返回错误 │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ 2. $decoded-&gt;type === &#39;refresh&#39; 校验必须为 refresh 类型 │</span></span>
<span class="line"><span>│ │ 非 refresh → 返回错误 │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ 3. User::find($decoded-&gt;uid) 校验用户仍存在且状态正常 │</span></span>
<span class="line"><span>│ │ 不存在/禁用 → 返回错误 │</span></span>
<span class="line"><span>│ ▼ │</span></span>
<span class="line"><span>│ 4. 签发新 access_token + 新 refresh_token │</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>
<span class="line"><span>│ localStorage.setItem(&#39;access_token&#39;, newAccessToken) │</span></span>
<span class="line"><span>│ localStorage.setItem(&#39;refresh_token&#39;, newRefreshToken) │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ // 用新 Token 重试原失败请求 │</span></span>
<span class="line"><span>│ config.headers[&#39;Authorization&#39;] = \`Bearer \${newAccessToken}\`│</span></span>
<span class="line"><span>│ return axios(config) │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────────┘</span></span></code></pre></div><h3 id="Token-刷新失败-重新登录-" tabindex="-1">Token 刷新失败(重新登录) <a class="header-anchor" href="#Token-刷新失败-重新登录-" aria-label="Permalink to &quot;Token 刷新失败(重新登录)&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>前端请求 API → 后端返回 code=401</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>尝试用 refresh_token 刷新</span></span>
<span class="line"><span> │</span></span>
<span class="line"><span> ▼</span></span>
<span class="line"><span>刷新失败(refresh_token 也过期/无效)</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>│ localStorage.removeItem(&#39;access_token&#39;) │</span></span>
<span class="line"><span>│ localStorage.removeItem(&#39;refresh_token&#39;) │</span></span>
<span class="line"><span>│ window.location.href = &#39;/login&#39; │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────────┘</span></span></code></pre></div><h3 id="两种-Token-的区别" tabindex="-1">两种 Token 的区别 <a class="header-anchor" href="#两种-Token-的区别" aria-label="Permalink to &quot;两种 Token 的区别&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>┌─────────────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│ access_token │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ 用途:接口认证(每个 API 请求携带) │</span></span>
<span class="line"><span>│ 有效期:2 小时(7200 秒) │</span></span>
<span class="line"><span>│ payload.type:access │</span></span>
<span class="line"><span>│ 传递方式:Authorization: Bearer &lt;token&gt; │</span></span>
<span class="line"><span>│ 校验方:AuthMiddleware │</span></span>
<span class="line"><span>│ 特点:短期有效,泄露影响范围有限 │</span></span>
<span class="line"><span>├─────────────────────────────────────────────────────────────┤</span></span>
<span class="line"><span>│ refresh_token │</span></span>
<span class="line"><span>│ │</span></span>
<span class="line"><span>│ 用途:刷新令牌(仅在 access_token 过期时使用) │</span></span>
<span class="line"><span>│ 有效期:7 天(604800 秒) │</span></span>
<span class="line"><span>│ payload.type:refresh │</span></span>
<span class="line"><span>│ 传递方式:POST /api/oauth2/token 请求体 │</span></span>
<span class="line"><span>│ 校验方:JwtService::refreshToken() │</span></span>
<span class="line"><span>│ 特点:长期有效,仅用于换取新令牌,不用于接口认证 │</span></span>
<span class="line"><span>└─────────────────────────────────────────────────────────────┘</span></span></code></pre></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>Authorization: Bearer &lt;access_token&gt;</span></span></code></pre></div><h3 id="示例" tabindex="-1">示例 <a class="header-anchor" href="#示例" aria-label="Permalink to &quot;示例&quot;">​</a></h3><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</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;"># curl</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">curl</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> http://localhost:8000/api/user/page</span><span style="--shiki-dark:#F286C4;--shiki-light:#A65E2B;"> \\</span></span>
<span class="line"><span style="--shiki-dark:#BF9EEE;--shiki-light:#A65E2B;"> -H</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># curl(带分页参数)</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">curl</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">http://localhost:8000/api/user/page?pageNo=1&amp;pageSize=10</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#A65E2B;"> \\</span></span>
<span class="line"><span style="--shiki-dark:#BF9EEE;--shiki-light:#A65E2B;"> -H</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span></span></code></pre></div><h3 id="请求头配置" tabindex="-1">请求头配置 <a class="header-anchor" href="#请求头配置" aria-label="Permalink to &quot;请求头配置&quot;">​</a></h3><table tabindex="0"><thead><tr><th>配置项</th><th>默认值</th><th>说明</th></tr></thead><tbody><tr><td><code>jwt.header_name</code></td><td><code>Authorization</code></td><td>请求头名称</td></tr><tr><td><code>jwt.header_prefix</code></td><td><code>Bearer </code></td><td>请求头前缀(注意有空格)</td></tr></tbody></table><h2 id="Token-获取" tabindex="-1">Token 获取 <a class="header-anchor" href="#Token-获取" aria-label="Permalink to &quot;Token 获取&quot;">​</a></h2><h3 id="登录获取" tabindex="-1">登录获取 <a class="header-anchor" href="#登录获取" aria-label="Permalink to &quot;登录获取&quot;">​</a></h3><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">POST</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/login</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">Content-Type:</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> application/json</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> &quot;username&quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">admin</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">,</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> &quot;password&quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">123456</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">,</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> &quot;code&quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">a3Bx</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">,</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> &quot;key&quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">captcha_key_xxx</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><p><strong>成功响应:</strong></p><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">code</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 0</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">ok</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;"> true</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">msg</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">登录成功</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">data</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">accessToken</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">refreshToken</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">tokenType</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">Bearer</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">expiresIn</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 7200</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">user</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">id</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">username</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">admin</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">realname</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">管理员</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">avatar</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">/uploads/user/avatar/avatar.jpg</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="OAuth2-密码模式获取" tabindex="-1">OAuth2 密码模式获取 <a class="header-anchor" href="#OAuth2-密码模式获取" aria-label="Permalink to &quot;OAuth2 密码模式获取&quot;">​</a></h3><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">POST</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/oauth2/token</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">Content-Type:</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> application/x-www-form-urlencoded</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#BF9EEE;--shiki-light:#B07D48;">grant_type</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">password</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">&amp;</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#B07D48;">username</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">admin</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">&amp;</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#B07D48;">password</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">123456</span></span></code></pre></div><h3 id="响应字段" tabindex="-1">响应字段 <a class="header-anchor" href="#响应字段" aria-label="Permalink to &quot;响应字段&quot;">​</a></h3><table tabindex="0"><thead><tr><th>字段</th><th>类型</th><th>说明</th></tr></thead><tbody><tr><td><code>accessToken</code></td><td>string</td><td>访问令牌(用于接口认证)</td></tr><tr><td><code>refreshToken</code></td><td>string</td><td>刷新令牌(用于换取新令牌)</td></tr><tr><td><code>tokenType</code></td><td>string</td><td>令牌类型(固定 <code>Bearer</code>)</td></tr><tr><td><code>expiresIn</code></td><td>number</td><td>access_token 有效期(秒,默认 7200)</td></tr><tr><td><code>user</code></td><td>object</td><td>用户基础信息(id、username、realname、avatar)</td></tr></tbody></table><h2 id="Token-刷新" tabindex="-1">Token 刷新 <a class="header-anchor" href="#Token-刷新" aria-label="Permalink to &quot;Token 刷新&quot;">​</a></h2><p>access_token 过期后(响应 code=401),使用 refresh_token 换取新令牌对:</p><h3 id="接口信息" tabindex="-1">接口信息 <a class="header-anchor" href="#接口信息" aria-label="Permalink to &quot;接口信息&quot;">​</a></h3><table tabindex="0"><thead><tr><th>项目</th><th>说明</th></tr></thead><tbody><tr><td>URL</td><td><code>POST /api/oauth2/token</code></td></tr><tr><td>Content-Type</td><td><code>application/json</code> 或 <code>application/x-www-form-urlencoded</code></td></tr></tbody></table><h3 id="请求示例" tabindex="-1">请求示例 <a class="header-anchor" href="#请求示例" aria-label="Permalink to &quot;请求示例&quot;">​</a></h3><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</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;"># JSON 格式</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">POST</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/oauth2/token</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">Content-Type:</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> application/json</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> &quot;grant_type&quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">refresh_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">,</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> &quot;refresh_token&quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># 表单格式</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">POST</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/oauth2/token</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">Content-Type:</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> application/x-www-form-urlencoded</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#BF9EEE;--shiki-light:#B07D48;">grant_type</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">refresh_token</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">&amp;</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#B07D48;">refresh_token</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">=</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">eyJ0eXAiOi...</span></span></code></pre></div><h3 id="成功响应" tabindex="-1">成功响应 <a class="header-anchor" href="#成功响应" aria-label="Permalink to &quot;成功响应&quot;">​</a></h3><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">code</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 0</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">ok</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;"> true</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">msg</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">success</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">data</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">accessToken</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">eyJ...(新access_token)</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">refreshToken</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">eyJ...(新refresh_token)</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">tokenType</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">Bearer</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">expiresIn</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 7200</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="失败响应" tabindex="-1">失败响应 <a class="header-anchor" href="#失败响应" aria-label="Permalink to &quot;失败响应&quot;">​</a></h3><table tabindex="0"><thead><tr><th>场景</th><th>code</th><th>msg</th></tr></thead><tbody><tr><td>缺少 refresh_token</td><td>1</td><td>缺少refresh_token参数</td></tr><tr><td>refresh_token 无效/过期</td><td>1</td><td>无效的refresh_token</td></tr><tr><td>用户不存在/禁用</td><td>1</td><td>用户不存在或已被禁用</td></tr></tbody></table><h2 id="Token-有效性检查" tabindex="-1">Token 有效性检查 <a class="header-anchor" href="#Token-有效性检查" aria-label="Permalink to &quot;Token 有效性检查&quot;">​</a></h2><h3 id="接口信息-1" tabindex="-1">接口信息 <a class="header-anchor" href="#接口信息-1" aria-label="Permalink to &quot;接口信息&quot;">​</a></h3><table tabindex="0"><thead><tr><th>项目</th><th>说明</th></tr></thead><tbody><tr><td>URL</td><td><code>GET /api/oauth2/check/token</code></td></tr><tr><td>权限</td><td>无需认证</td></tr></tbody></table><h3 id="请求示例-1" tabindex="-1">请求示例 <a class="header-anchor" href="#请求示例-1" aria-label="Permalink to &quot;请求示例&quot;">​</a></h3><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</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;"># 通过请求头传递</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">GET</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/oauth2/check/token</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">Authorization:</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> Bearer</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> &lt;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access_toke</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">n</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">&gt;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"># 通过 URL 参数传递</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">GET</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/oauth2/check/token?token=</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">&lt;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access_toke</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">n</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">&gt;</span></span></code></pre></div><h3 id="成功响应-有效-" tabindex="-1">成功响应(有效) <a class="header-anchor" href="#成功响应-有效-" aria-label="Permalink to &quot;成功响应(有效)&quot;">​</a></h3><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">code</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 0</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">ok</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;"> true</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">msg</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">操作成功</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">data</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">valid</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;"> true</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">uid</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">exp</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1727007200</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="失败响应-无效-" tabindex="-1">失败响应(无效) <a class="header-anchor" href="#失败响应-无效-" aria-label="Permalink to &quot;失败响应(无效)&quot;">​</a></h3><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">code</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 0</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">ok</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;"> true</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">msg</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">操作成功</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">data</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">valid</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;"> false</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">msg</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">token已过期</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h2 id="Token-吊销" tabindex="-1">Token 吊销 <a class="header-anchor" href="#Token-吊销" aria-label="Permalink to &quot;Token 吊销&quot;">​</a></h2><table tabindex="0"><thead><tr><th>项目</th><th>说明</th></tr></thead><tbody><tr><td>URL</td><td><code>DELETE /api/oauth2/remove/{token}</code></td></tr></tbody></table><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">DELETE</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> /api/oauth2/remove/eyJ0eXAiOi...</span></span>
<span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">Authorization:</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;"> Bearer</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> &lt;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access_toke</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">n</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;">&gt;</span></span></code></pre></div><div class="warning custom-block"><p class="custom-block-title">简化实现</p><p>当前 Token 吊销为简化实现(直接返回 true),JWT 本身无状态,服务端无法直接使其失效。生产环境建议结合 Redis 黑名单机制实现真正的吊销。</p></div><h2 id="URL-参数传递-SSO-场景-" tabindex="-1">URL 参数传递(SSO 场景) <a class="header-anchor" href="#URL-参数传递-SSO-场景-" aria-label="Permalink to &quot;URL 参数传递(SSO 场景)&quot;">​</a></h2><p>路由守卫支持通过 URL 参数传递 Token,适用于 SSO 单点登录场景:</p><div class="language-html vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">html</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#393A34;">http://localhost:8001/?token=</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">&lt;</span><span style="--shiki-dark:#EE6666;--shiki-dark-font-style:italic;--shiki-dark-text-decoration:underline;--shiki-light:#B31D28;--shiki-light-font-style:inherit;--shiki-light-text-decoration:inherit;">access_token</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">&gt;</span></span></code></pre></div><p><strong>前端处理逻辑:</strong></p><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>1. 路由守卫检测 URL 中的 token 参数</span></span>
<span class="line"><span>2. 将 token 存入 localStorage</span></span>
<span class="line"><span>3. 清除 URL 中的 token 参数(防止泄露)</span></span>
<span class="line"><span>4. 后续请求自动携带该 token</span></span></code></pre></div><h2 id="前端存储与使用" tabindex="-1">前端存储与使用 <a class="header-anchor" href="#前端存储与使用" aria-label="Permalink to &quot;前端存储与使用&quot;">​</a></h2><h3 id="存储" tabindex="-1">存储 <a class="header-anchor" href="#存储" aria-label="Permalink to &quot;存储&quot;">​</a></h3><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</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;">// 登录后存储</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">setItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> data</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">accessToken</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">setItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">refresh_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> data</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">refreshToken</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;">// 登出时清除</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">removeItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">removeItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">refresh_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span></code></pre></div><h3 id="请求拦截器-自动携带-" tabindex="-1">请求拦截器(自动携带) <a class="header-anchor" href="#请求拦截器-自动携带-" aria-label="Permalink to &quot;请求拦截器(自动携带)&quot;">​</a></h3><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</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;">// src/utils/http/axios/index.ts</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">axios</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">interceptors</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">request</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">use</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#FFB86C;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;">config</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =&gt;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> token</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">getItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> if</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> (</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">token</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> config</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">headers</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">[</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">Authorization</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">]</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B5695977;"> \`</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">Bearer </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">\${</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B56959;">token</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">}</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B5695977;">\`</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> return</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> config</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">});</span></span></code></pre></div><h3 id="响应拦截器-自动刷新-" tabindex="-1">响应拦截器(自动刷新) <a class="header-anchor" href="#响应拦截器-自动刷新-" aria-label="Permalink to &quot;响应拦截器(自动刷新)&quot;">​</a></h3><div class="language-typescript vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">typescript</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">axios</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">interceptors</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">response</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">use</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span></span>
<span class="line"><span style="--shiki-dark:#FFB86C;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;"> response</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =&gt;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> response</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> async</span><span style="--shiki-dark:#FFB86C;--shiki-dark-font-style:italic;--shiki-light:#B07D48;--shiki-light-font-style:inherit;"> error</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =&gt;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> config</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> response</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> error</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> if</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> (</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">response</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">?.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">status</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> ===</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 401</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> &amp;&amp;</span><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> !</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">config</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">_retry</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> config</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">_retry</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#1E754F;"> true</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> refreshToken</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">getItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">refresh_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> if</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> (</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">refreshToken</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> try</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 尝试刷新 Token</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#AB5959;"> const</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> res</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> await</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> refreshTokenApi</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">refreshToken</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">setItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> res</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">data</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">accessToken</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">setItem</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">refresh_token</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> res</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">data</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">refreshToken</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 用新 Token 重试原请求</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> config</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">headers</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">[</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">Authorization</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">]</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B5695977;"> \`</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">Bearer </span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">\${</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B56959;">res</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B56959;">data</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B56959;">accessToken</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;">}</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B5695977;">\`</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> return</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;"> axios</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">config</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> catch</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> (</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">e</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">)</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> {</span></span>
<span class="line"><span style="--shiki-dark:#7B7F8B;--shiki-light:#A0ADA0;"> // 刷新失败,跳转登录</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> localStorage</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">clear</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">();</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;"> window</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">location</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">href</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;"> =</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &#39;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">/login</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&#39;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">;</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"></span>
<span class="line"><span style="--shiki-dark:#F286C4;--shiki-light:#1E754F;"> return</span><span style="--shiki-dark:#97E1F1;--shiki-dark-font-style:italic;--shiki-light:#998418;--shiki-light-font-style:inherit;"> Promise</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">.</span><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">reject</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">(</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#B07D48;">error</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;"> }</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">);</span></span></code></pre></div><h2 id="Token-Payload-结构" tabindex="-1">Token Payload 结构 <a class="header-anchor" href="#Token-Payload-结构" aria-label="Permalink to &quot;Token Payload 结构&quot;">​</a></h2><h3 id="access-token" tabindex="-1">access_token <a class="header-anchor" href="#access-token" aria-label="Permalink to &quot;access_token&quot;">​</a></h3><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">uid</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">username</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">admin</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">type</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">access</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">iss</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">rxthinkcmf</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">iat</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1727000000</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">exp</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1727007200</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></div><h3 id="refresh-token" tabindex="-1">refresh_token <a class="header-anchor" href="#refresh-token" aria-label="Permalink to &quot;refresh_token&quot;">​</a></h3><div class="language-json vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">json</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">{</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">uid</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">username</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">admin</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">type</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">refresh</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">iss</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">rxthinkcmf</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">iat</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1727000000</span><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">,</span></span>
<span class="line"><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;"> &quot;</span><span style="--shiki-dark:#97E1F1;--shiki-light:#998418;">exp</span><span style="--shiki-dark:#97E2F2;--shiki-light:#99841877;">&quot;</span><span style="--shiki-dark:#F286C4;--shiki-light:#999999;">:</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#2F798A;"> 1727604800</span></span>
<span class="line"><span style="--shiki-dark:#F6F6F4;--shiki-light:#999999;">}</span></span></code></pre></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>HTTP 状态</th><th>code</th><th>msg</th></tr></thead><tbody><tr><td>未提供 Token</td><td>200</td><td>401</td><td>未提供认证令牌</td></tr><tr><td>Token 过期</td><td>200</td><td>401</td><td>token已过期</td></tr><tr><td>Token 签名无效</td><td>200</td><td>401</td><td>token签名无效</td></tr><tr><td>Token 类型错误</td><td>200</td><td>401</td><td>无效的认证令牌</td></tr><tr><td>无权限</td><td>200</td><td>403</td><td>无访问权限</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><th>说明</th></tr></thead><tbody><tr><td><code>jwt.secret</code></td><td><code>JWT.SECRET</code></td><td>内置默认密钥</td><td>JWT 签名密钥,<strong>生产环境务必修改</strong></td></tr><tr><td><code>jwt.access_ttl</code></td><td><code>JWT.ACCESS_TTL</code></td><td><code>7200</code></td><td>access_token 有效期(秒)</td></tr><tr><td><code>jwt.refresh_ttl</code></td><td><code>JWT.REFRESH_TTL</code></td><td><code>604800</code></td><td>refresh_token 有效期(秒)</td></tr><tr><td><code>jwt.issuer</code></td><td>—</td><td><code>rxthinkcmf</code></td><td>签发者标识</td></tr><tr><td><code>jwt.header_name</code></td><td>—</td><td><code>Authorization</code></td><td>请求头名称</td></tr><tr><td><code>jwt.header_prefix</code></td><td>—</td><td><code>Bearer </code></td><td>请求头前缀</td></tr></tbody></table><div class="warning custom-block"><p class="custom-block-title">生产环境安全</p><p>生产环境务必通过 <code>.env</code> 设置 <code>JWT.SECRET</code> 为强随机密钥:</p><div class="language-bash vp-adaptive-theme"><button title="Copy Code" class="copy"></button><span class="lang">bash</span><pre class="shiki shiki-themes dracula-soft vitesse-light vp-code" tabindex="0"><code><span class="line"><span style="--shiki-dark:#62E884;--shiki-light:#59873A;">php</span><span style="--shiki-dark:#BF9EEE;--shiki-light:#A65E2B;"> -r</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;"> &quot;</span><span style="--shiki-dark:#E7EE98;--shiki-light:#B56959;">echo bin2hex(random_bytes(32));</span><span style="--shiki-dark:#DEE492;--shiki-light:#B5695977;">&quot;</span></span></code></pre></div></div>`,73)])])}const c=i(p,[["render",h]]);export{F as __pageData,c as default};