fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier) - Relaxed strict Vue/JS rules for demo code compatibility - Fix syntax errors in ApiPlayground and VoiceCloningDemo - Fix duplicate else-if condition in ApiPlayground - Fix Promise executor async pattern in AutoregressiveAudioDemo - Add TypeScript file support to ESLint config Warnings reduced from 295 to 251 problems. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,9 @@
|
||||
<template>
|
||||
<div class="auth-basics-demo">
|
||||
<div class="header">
|
||||
<div class="title">🧰 鉴权的 4 种常见“凭证”</div>
|
||||
<div class="title">
|
||||
🧰 鉴权的 4 种常见“凭证”
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
选一个方案,看看请求长什么样、优缺点是什么、最常见坑是什么。
|
||||
</div>
|
||||
@@ -26,24 +28,44 @@
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-title">请求长什么样</div>
|
||||
<div class="card-title">
|
||||
请求长什么样
|
||||
</div>
|
||||
<pre class="code"><code>{{ active.example }}</code></pre>
|
||||
<div class="hint">{{ active.note }}</div>
|
||||
<div class="hint">
|
||||
{{ active.note }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">什么时候用 / 不用</div>
|
||||
<div class="card-title">
|
||||
什么时候用 / 不用
|
||||
</div>
|
||||
<div class="two">
|
||||
<div class="box">
|
||||
<div class="box-title">✅ 适合</div>
|
||||
<div class="box-title">
|
||||
✅ 适合
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li v-for="(x, i) in active.pros" :key="i">{{ x }}</li>
|
||||
<li
|
||||
v-for="(x, i) in active.pros"
|
||||
:key="i"
|
||||
>
|
||||
{{ x }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-title">⚠️ 不适合 / 风险</div>
|
||||
<div class="box-title">
|
||||
⚠️ 不适合 / 风险
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li v-for="(x, i) in active.cons" :key="i">{{ x }}</li>
|
||||
<li
|
||||
v-for="(x, i) in active.cons"
|
||||
:key="i"
|
||||
>
|
||||
{{ x }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -51,10 +73,11 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">一句话口诀</div>
|
||||
<div class="card-title">
|
||||
一句话口诀
|
||||
</div>
|
||||
<div class="desc">
|
||||
<strong>先认证(你是谁)</strong
|
||||
>,再授权(你能做什么)。凭证只是“证明身份的方式”,授权永远要在服务端执行。
|
||||
<strong>先认证(你是谁)</strong>,再授权(你能做什么)。凭证只是“证明身份的方式”,授权永远要在服务端执行。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,12 @@
|
||||
<template>
|
||||
<div class="auth-evolution-demo">
|
||||
<div class="header">
|
||||
<div class="title">🧭 鉴权方案演进:从 Basic 到 OAuth2</div>
|
||||
<div class="subtitle">点击卡片,快速建立“场景 → 方案”的直觉。</div>
|
||||
<div class="title">
|
||||
🧭 鉴权方案演进:从 Basic 到 OAuth2
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
点击卡片,快速建立“场景 → 方案”的直觉。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="timeline">
|
||||
@@ -21,25 +25,45 @@
|
||||
<span class="icon">{{ s.icon }}</span>
|
||||
<span class="name">{{ s.name }}</span>
|
||||
</div>
|
||||
<div class="stage-sub">{{ s.when }}</div>
|
||||
<div class="stage-sub">
|
||||
{{ s.when }}
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">{{ active.icon }} {{ active.name }}</div>
|
||||
<div class="desc">{{ active.desc }}</div>
|
||||
<div class="card-title">
|
||||
{{ active.icon }} {{ active.name }}
|
||||
</div>
|
||||
<div class="desc">
|
||||
{{ active.desc }}
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="box">
|
||||
<div class="box-title">✅ 适合</div>
|
||||
<div class="box-title">
|
||||
✅ 适合
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li v-for="(x, i) in active.pros" :key="i">{{ x }}</li>
|
||||
<li
|
||||
v-for="(x, i) in active.pros"
|
||||
:key="i"
|
||||
>
|
||||
{{ x }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-title">⚠️ 主要风险</div>
|
||||
<div class="box-title">
|
||||
⚠️ 主要风险
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li v-for="(x, i) in active.cons" :key="i">{{ x }}</li>
|
||||
<li
|
||||
v-for="(x, i) in active.cons"
|
||||
:key="i"
|
||||
>
|
||||
{{ x }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+117
-55
@@ -13,13 +13,19 @@
|
||||
<template>
|
||||
<div class="auth-interactive-login">
|
||||
<div class="header">
|
||||
<div class="title">🔐 认证流程演示</div>
|
||||
<div class="subtitle">模拟登录过程,理解认证与授权的区别</div>
|
||||
<div class="title">
|
||||
🔐 认证流程演示
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
模拟登录过程,理解认证与授权的区别
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 模式切换 -->
|
||||
<div class="mode-selector">
|
||||
<div class="mode-label">选择鉴权方式:</div>
|
||||
<div class="mode-label">
|
||||
选择鉴权方式:
|
||||
</div>
|
||||
<div class="mode-buttons">
|
||||
<button
|
||||
class="mode-btn"
|
||||
@@ -42,110 +48,140 @@
|
||||
<!-- 登录表单 -->
|
||||
<div class="login-section">
|
||||
<div class="form-container">
|
||||
<div class="form-title">登录表单</div>
|
||||
<div class="form-title">
|
||||
登录表单
|
||||
</div>
|
||||
<div class="form-fields">
|
||||
<div class="field-group">
|
||||
<label>用户名</label>
|
||||
<input
|
||||
type="text"
|
||||
v-model="username"
|
||||
type="text"
|
||||
placeholder="输入用户名"
|
||||
:disabled="locked"
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
<div class="field-group">
|
||||
<label>密码</label>
|
||||
<input
|
||||
type="password"
|
||||
v-model="password"
|
||||
type="password"
|
||||
placeholder="输入密码"
|
||||
:disabled="locked"
|
||||
@keyup.enter="startDemo"
|
||||
/>
|
||||
>
|
||||
</div>
|
||||
<button
|
||||
class="login-btn"
|
||||
@click="startDemo"
|
||||
:disabled="!username || !password || locked"
|
||||
@click="startDemo"
|
||||
>
|
||||
开始演示
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="hints">
|
||||
<div class="hint-title">💡 提示</div>
|
||||
<div class="hint-title">
|
||||
💡 提示
|
||||
</div>
|
||||
<div class="hint-text">
|
||||
试试用户名 <code>admin</code>,密码 <code>123456</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stepper" v-if="flowStep > 0">
|
||||
<div
|
||||
v-if="flowStep > 0"
|
||||
class="stepper"
|
||||
>
|
||||
<div class="stepper-title">
|
||||
当前步骤:{{ flowStep }} / {{ maxStep }}
|
||||
<span class="stepper-hint"
|
||||
>(手动推进,避免“自动下一步”误解)</span
|
||||
>
|
||||
<span class="stepper-hint">(手动推进,避免“自动下一步”误解)</span>
|
||||
</div>
|
||||
<div class="stepper-actions">
|
||||
<button
|
||||
class="step-btn"
|
||||
@click="prevStep"
|
||||
:disabled="flowStep <= 1"
|
||||
@click="prevStep"
|
||||
>
|
||||
上一步
|
||||
</button>
|
||||
<button
|
||||
class="step-btn primary"
|
||||
@click="nextStep"
|
||||
:disabled="flowStep >= maxStep"
|
||||
@click="nextStep"
|
||||
>
|
||||
下一步
|
||||
</button>
|
||||
<button class="step-btn" @click="resetDemo">重置</button>
|
||||
<button
|
||||
class="step-btn"
|
||||
@click="resetDemo"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 实时数据流 -->
|
||||
<div class="data-flow">
|
||||
<div class="flow-title">📊 数据流可视化</div>
|
||||
<div class="flow-title">
|
||||
📊 数据流可视化
|
||||
</div>
|
||||
|
||||
<!-- 请求阶段 -->
|
||||
<div class="flow-stage request-stage" v-if="currentStage >= 1">
|
||||
<div
|
||||
v-if="currentStage >= 1"
|
||||
class="flow-stage request-stage"
|
||||
>
|
||||
<div class="stage-header">
|
||||
<span class="stage-badge">{{
|
||||
currentStage === 1 ? '📤' : '✅'
|
||||
}}</span>
|
||||
<span class="stage-name">1. 客户端发送登录请求</span>
|
||||
</div>
|
||||
<div class="request-content" v-if="currentStage >= 1">
|
||||
<div
|
||||
v-if="currentStage >= 1"
|
||||
class="request-content"
|
||||
>
|
||||
<div class="request-line">
|
||||
<span class="method">POST</span>
|
||||
<span class="path">/api/login</span>
|
||||
</div>
|
||||
<div class="request-body">
|
||||
<div class="body-title">Body:</div>
|
||||
<div class="body-title">
|
||||
Body:
|
||||
</div>
|
||||
<pre>
|
||||
{
|
||||
"username": "{{ username }}",
|
||||
"password": "******"
|
||||
}</pre
|
||||
>
|
||||
}</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-arrow" v-if="currentStage >= 1">⬇️</div>
|
||||
<div
|
||||
v-if="currentStage >= 1"
|
||||
class="flow-arrow"
|
||||
>
|
||||
⬇️
|
||||
</div>
|
||||
|
||||
<!-- 服务器验证阶段 -->
|
||||
<div class="flow-stage server-stage" v-if="currentStage >= 2">
|
||||
<div
|
||||
v-if="currentStage >= 2"
|
||||
class="flow-stage server-stage"
|
||||
>
|
||||
<div class="stage-header">
|
||||
<span class="stage-badge">{{
|
||||
currentStage === 2 ? '⚙️' : '✅'
|
||||
}}</span>
|
||||
<span class="stage-name">2. 服务器验证身份</span>
|
||||
</div>
|
||||
<div class="server-content" v-if="currentStage >= 2">
|
||||
<div
|
||||
v-if="currentStage >= 2"
|
||||
class="server-content"
|
||||
>
|
||||
<div class="verification-steps">
|
||||
<div
|
||||
class="verify-step"
|
||||
@@ -172,30 +208,43 @@
|
||||
<span class="step-icon">{{
|
||||
verificationStep >= 3 ? '✅' : '⏳'
|
||||
}}</span>
|
||||
<span class="step-text"
|
||||
>生成{{
|
||||
mode === 'session' ? 'Session' : 'JWT Token'
|
||||
}}</span
|
||||
>
|
||||
<span class="step-text">生成{{
|
||||
mode === 'session' ? 'Session' : 'JWT Token'
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flow-arrow" v-if="currentStage >= 2">⬇️</div>
|
||||
<div
|
||||
v-if="currentStage >= 2"
|
||||
class="flow-arrow"
|
||||
>
|
||||
⬇️
|
||||
</div>
|
||||
|
||||
<!-- 响应阶段 -->
|
||||
<div class="flow-stage response-stage" v-if="currentStage >= 3">
|
||||
<div
|
||||
v-if="currentStage >= 3"
|
||||
class="flow-stage response-stage"
|
||||
>
|
||||
<div class="stage-header">
|
||||
<span class="stage-badge">{{
|
||||
currentStage === 3 ? '📥' : '✅'
|
||||
}}</span>
|
||||
<span class="stage-name">3. 服务器返回认证结果</span>
|
||||
</div>
|
||||
<div class="response-content" v-if="authResult">
|
||||
<div class="response-status success">✅ 登录成功</div>
|
||||
<div
|
||||
v-if="authResult"
|
||||
class="response-content"
|
||||
>
|
||||
<div class="response-status success">
|
||||
✅ 登录成功
|
||||
</div>
|
||||
<div class="response-body">
|
||||
<div class="body-title">Response:</div>
|
||||
<div class="body-title">
|
||||
Response:
|
||||
</div>
|
||||
<pre v-if="mode === 'session'">
|
||||
{
|
||||
"status": "success",
|
||||
@@ -203,8 +252,7 @@
|
||||
"id": 123,
|
||||
"username": "{{ username }}"
|
||||
}
|
||||
}</pre
|
||||
>
|
||||
}</pre>
|
||||
<pre v-else>
|
||||
{
|
||||
"status": "success",
|
||||
@@ -213,26 +261,24 @@
|
||||
"id": 123,
|
||||
"username": "{{ username }}"
|
||||
}
|
||||
}</pre
|
||||
>
|
||||
}</pre>
|
||||
</div>
|
||||
<div class="auth-mechanism" v-if="currentStage >= 4">
|
||||
<div
|
||||
v-if="currentStage >= 4"
|
||||
class="auth-mechanism"
|
||||
>
|
||||
<div class="mechanism-title">
|
||||
{{ mode === 'session' ? '🍪 Cookie 设置' : '🎫 Token 存储' }}
|
||||
</div>
|
||||
<div class="mechanism-content">
|
||||
<div v-if="mode === 'session'">
|
||||
<code
|
||||
>Set-Cookie: session_id={{ authResult?.sessionId }};
|
||||
HttpOnly; Secure</code
|
||||
>
|
||||
<code>Set-Cookie: session_id={{ authResult?.sessionId }};
|
||||
HttpOnly; Secure</code>
|
||||
</div>
|
||||
<div v-else>
|
||||
<code
|
||||
>localStorage.setItem("token", "{{
|
||||
authResult?.token
|
||||
}}")</code
|
||||
>
|
||||
<code>localStorage.setItem("token", "{{
|
||||
authResult?.token
|
||||
}}")</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -240,7 +286,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 后续请求 -->
|
||||
<div class="flow-stage request-stage" v-if="currentStage >= 5">
|
||||
<div
|
||||
v-if="currentStage >= 5"
|
||||
class="flow-stage request-stage"
|
||||
>
|
||||
<div class="stage-header">
|
||||
<span class="stage-badge">🔄</span>
|
||||
<span class="stage-name">4. 后续请求自动携带认证信息</span>
|
||||
@@ -251,7 +300,9 @@
|
||||
<span class="path">/api/user/profile</span>
|
||||
</div>
|
||||
<div class="auth-header">
|
||||
<div class="header-title">Header:</div>
|
||||
<div class="header-title">
|
||||
Header:
|
||||
</div>
|
||||
<div v-if="mode === 'session'">
|
||||
<code>Cookie: session_id={{ authResult?.sessionId }}</code>
|
||||
</div>
|
||||
@@ -266,7 +317,10 @@
|
||||
</div>
|
||||
|
||||
<!-- 状态说明 -->
|
||||
<div class="state-description" v-if="currentStage >= 4">
|
||||
<div
|
||||
v-if="currentStage >= 4"
|
||||
class="state-description"
|
||||
>
|
||||
<div class="description-title">
|
||||
📖 {{ mode === 'session' ? 'Session' : 'JWT' }} 工作原理
|
||||
</div>
|
||||
@@ -287,8 +341,16 @@
|
||||
</div>
|
||||
|
||||
<!-- 重置按钮 -->
|
||||
<div class="reset-section" v-if="currentStage >= 5">
|
||||
<button class="reset-btn" @click="resetDemo">🔄 重新演示</button>
|
||||
<div
|
||||
v-if="currentStage >= 5"
|
||||
class="reset-section"
|
||||
>
|
||||
<button
|
||||
class="reset-btn"
|
||||
@click="resetDemo"
|
||||
>
|
||||
🔄 重新演示
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<template>
|
||||
<div class="authn-authz-demo">
|
||||
<div class="header">
|
||||
<div class="title">🪪 AuthN vs 🛂 AuthZ:一个请求到底会经历什么?</div>
|
||||
<div class="title">
|
||||
🪪 AuthN vs 🛂 AuthZ:一个请求到底会经历什么?
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
选择“谁在请求”与“要做什么”,看看认证/授权分别在哪一步起作用。
|
||||
</div>
|
||||
@@ -13,7 +15,9 @@
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-title">选择请求</div>
|
||||
<div class="card-title">
|
||||
选择请求
|
||||
</div>
|
||||
|
||||
<label class="label">身份(AuthN:你是谁)</label>
|
||||
<div class="row">
|
||||
@@ -48,18 +52,26 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">模拟结果</div>
|
||||
<div class="card-title">
|
||||
模拟结果
|
||||
</div>
|
||||
|
||||
<div class="result">
|
||||
<div class="line">
|
||||
<span class="k">AuthN(认证)</span>
|
||||
<span class="v" :class="authn.ok ? 'ok' : 'bad'">
|
||||
<span
|
||||
class="v"
|
||||
:class="authn.ok ? 'ok' : 'bad'"
|
||||
>
|
||||
{{ authn.ok ? '通过' : '失败' }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="line">
|
||||
<span class="k">AuthZ(授权)</span>
|
||||
<span class="v" :class="authz.ok ? 'ok' : 'bad'">
|
||||
<span
|
||||
class="v"
|
||||
:class="authz.ok ? 'ok' : 'bad'"
|
||||
>
|
||||
{{ authz.ok ? '允许' : '拒绝' }}
|
||||
</span>
|
||||
</div>
|
||||
@@ -74,7 +86,9 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">关键点</div>
|
||||
<div class="card-title">
|
||||
关键点
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li><strong>认证失败:</strong>你是谁都不确定 → 通常返回 401。</li>
|
||||
<li>
|
||||
@@ -82,8 +96,7 @@
|
||||
403。
|
||||
</li>
|
||||
<li>
|
||||
<strong>授权规则要在服务端:</strong
|
||||
>别相信前端的“是否显示按钮”,那只是 UX。
|
||||
<strong>授权规则要在服务端:</strong>别相信前端的“是否显示按钮”,那只是 UX。
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<template>
|
||||
<div class="csrf-demo">
|
||||
<div class="header">
|
||||
<div class="title">🛡️ CSRF:为什么“自动带 Cookie”会出事?</div>
|
||||
<div class="title">
|
||||
🛡️ CSRF:为什么“自动带 Cookie”会出事?
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
手动推进一个最小攻击链,再看 3 个最常用防护手段(SameSite / CSRF Token /
|
||||
双重提交)。
|
||||
@@ -13,51 +15,78 @@
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn primary" @click="start" :disabled="step !== 0">
|
||||
开始
|
||||
</button>
|
||||
<button class="btn" @click="prev" :disabled="step <= 1">上一步</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
@click="next"
|
||||
:disabled="step !== 0"
|
||||
@click="start"
|
||||
>
|
||||
开始
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
:disabled="step <= 1"
|
||||
@click="prev"
|
||||
>
|
||||
上一步
|
||||
</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
:disabled="step === 0 || step >= maxStep"
|
||||
@click="next"
|
||||
>
|
||||
下一步
|
||||
</button>
|
||||
<button class="btn" @click="reset">重置</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="reset"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="step > 0" class="progress">
|
||||
<div
|
||||
v-if="step > 0"
|
||||
class="progress"
|
||||
>
|
||||
Step {{ step }} / {{ maxStep }} · {{ steps[step - 1]?.title }}
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-title">场景</div>
|
||||
<div class="card-title">
|
||||
场景
|
||||
</div>
|
||||
<div class="desc">
|
||||
假设你登录了 <strong>bank.com</strong>(Cookie
|
||||
已存在)。你又打开了一个恶意网站
|
||||
<strong>evil.com</strong>,它偷偷发起转账请求。
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-title">你的 Cookie(浏览器会自动带)</div>
|
||||
<div class="box-title">
|
||||
你的 Cookie(浏览器会自动带)
|
||||
</div>
|
||||
<code class="mono">Cookie: session_id=abc123</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">本步请求</div>
|
||||
<div class="card-title">
|
||||
本步请求
|
||||
</div>
|
||||
<pre class="code"><code>{{ requestText }}</code></pre>
|
||||
<div class="desc">{{ steps[step - 1]?.desc }}</div>
|
||||
<div class="desc">
|
||||
{{ steps[step - 1]?.desc }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">防护怎么选?(优先顺序)</div>
|
||||
<div class="card-title">
|
||||
防护怎么选?(优先顺序)
|
||||
</div>
|
||||
<ol class="list">
|
||||
<li>
|
||||
<strong>SameSite Cookie:</strong
|
||||
>对大多数“跨站表单/图片”请求非常有效(Lax/Strict)。
|
||||
<strong>SameSite Cookie:</strong>对大多数“跨站表单/图片”请求非常有效(Lax/Strict)。
|
||||
</li>
|
||||
<li>
|
||||
<strong>CSRF Token:</strong>在表单/请求头里带
|
||||
@@ -69,7 +98,9 @@
|
||||
</li>
|
||||
</ol>
|
||||
<div class="warn">
|
||||
<div class="warn-title">注意</div>
|
||||
<div class="warn-title">
|
||||
注意
|
||||
</div>
|
||||
<div class="warn-text">
|
||||
CSRF 主要针对“Cookie 自动携带”的场景。若你用 Authorization:
|
||||
Bearer(不自动发送),CSRF 风险会显著降低,但仍要考虑 XSS/Token
|
||||
|
||||
@@ -5,34 +5,56 @@
|
||||
<template>
|
||||
<div class="jwt-workflow-demo">
|
||||
<div class="header">
|
||||
<div class="title">🎫 JWT:生成 → 发送 → 验证 → 解析</div>
|
||||
<div class="title">
|
||||
🎫 JWT:生成 → 发送 → 验证 → 解析
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
默认“手动推进”,不自动下一步;避免把演示误当成真实系统的安全边界。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn primary" @click="start" :disabled="step !== 0">
|
||||
开始
|
||||
</button>
|
||||
<button class="btn" @click="prev" :disabled="step <= 1">上一步</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
@click="next"
|
||||
:disabled="step !== 0"
|
||||
@click="start"
|
||||
>
|
||||
开始
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
:disabled="step <= 1"
|
||||
@click="prev"
|
||||
>
|
||||
上一步
|
||||
</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
:disabled="step === 0 || step >= maxStep"
|
||||
@click="next"
|
||||
>
|
||||
下一步
|
||||
</button>
|
||||
<button class="btn" @click="reset">重置</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="reset"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="step > 0" class="progress">
|
||||
<div
|
||||
v-if="step > 0"
|
||||
class="progress"
|
||||
>
|
||||
Step {{ step }} / {{ maxStep }} · {{ steps[step - 1]?.title }}
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-title">用户声明(Payload 示例)</div>
|
||||
<div class="card-title">
|
||||
用户声明(Payload 示例)
|
||||
</div>
|
||||
<pre class="code"><code>{{ payloadJson }}</code></pre>
|
||||
<div class="hint">
|
||||
注意:JWT 的 payload 只是 Base64Url
|
||||
@@ -41,45 +63,90 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">JWT Token(示意)</div>
|
||||
<div class="card-title">
|
||||
JWT Token(示意)
|
||||
</div>
|
||||
<div class="token">
|
||||
<div class="part" :class="{ active: step >= 1 }">
|
||||
<div class="part-label">Header</div>
|
||||
<div
|
||||
class="part"
|
||||
:class="{ active: step >= 1 }"
|
||||
>
|
||||
<div class="part-label">
|
||||
Header
|
||||
</div>
|
||||
<code class="mono">{{ step >= 1 ? headerB64 : '...' }}</code>
|
||||
</div>
|
||||
<div class="dot">.</div>
|
||||
<div class="part" :class="{ active: step >= 2 }">
|
||||
<div class="part-label">Payload</div>
|
||||
<div class="dot">
|
||||
.
|
||||
</div>
|
||||
<div
|
||||
class="part"
|
||||
:class="{ active: step >= 2 }"
|
||||
>
|
||||
<div class="part-label">
|
||||
Payload
|
||||
</div>
|
||||
<code class="mono">{{ step >= 2 ? payloadB64 : '...' }}</code>
|
||||
</div>
|
||||
<div class="dot">.</div>
|
||||
<div class="part" :class="{ active: step >= 3 }">
|
||||
<div class="part-label">Signature</div>
|
||||
<div class="dot">
|
||||
.
|
||||
</div>
|
||||
<div
|
||||
class="part"
|
||||
:class="{ active: step >= 3 }"
|
||||
>
|
||||
<div class="part-label">
|
||||
Signature
|
||||
</div>
|
||||
<code class="mono">{{ step >= 3 ? signatureB64 : '...' }}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mono-box" v-if="step >= 4">
|
||||
<div class="mono-label">完整 Token</div>
|
||||
<div
|
||||
v-if="step >= 4"
|
||||
class="mono-box"
|
||||
>
|
||||
<div class="mono-label">
|
||||
完整 Token
|
||||
</div>
|
||||
<code class="mono">{{ token }}</code>
|
||||
<button class="copy" @click="copy(token)">
|
||||
<button
|
||||
class="copy"
|
||||
@click="copy(token)"
|
||||
>
|
||||
{{ copied ? '已复制' : '复制 Token' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mono-box" v-if="step >= 5">
|
||||
<div class="mono-label">请求头示例</div>
|
||||
<div
|
||||
v-if="step >= 5"
|
||||
class="mono-box"
|
||||
>
|
||||
<div class="mono-label">
|
||||
请求头示例
|
||||
</div>
|
||||
<code class="mono">Authorization: Bearer {{ token }}</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">{{ steps[step - 1]?.title || '流程说明' }}</div>
|
||||
<div class="desc">{{ steps[step - 1]?.desc }}</div>
|
||||
<div v-if="steps[step - 1]?.warn" class="warn">
|
||||
<div class="warn-title">注意</div>
|
||||
<div class="warn-text">{{ steps[step - 1]?.warn }}</div>
|
||||
<div class="card-title">
|
||||
{{ steps[step - 1]?.title || '流程说明' }}
|
||||
</div>
|
||||
<div class="desc">
|
||||
{{ steps[step - 1]?.desc }}
|
||||
</div>
|
||||
<div
|
||||
v-if="steps[step - 1]?.warn"
|
||||
class="warn"
|
||||
>
|
||||
<div class="warn-title">
|
||||
注意
|
||||
</div>
|
||||
<div class="warn-text">
|
||||
{{ steps[step - 1]?.warn }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<template>
|
||||
<div class="oauth2-demo">
|
||||
<div class="header">
|
||||
<div class="title">🔑 OAuth2:第三方登录(授权码流程)</div>
|
||||
<div class="title">
|
||||
🔑 OAuth2:第三方登录(授权码流程)
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
用最常见的 Authorization Code Flow(建议配合
|
||||
PKCE)。默认手动推进,不自动下一步。
|
||||
@@ -13,34 +15,64 @@
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn primary" @click="start" :disabled="step !== 0">
|
||||
开始
|
||||
</button>
|
||||
<button class="btn" @click="prev" :disabled="step <= 1">上一步</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
@click="next"
|
||||
:disabled="step !== 0"
|
||||
@click="start"
|
||||
>
|
||||
开始
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
:disabled="step <= 1"
|
||||
@click="prev"
|
||||
>
|
||||
上一步
|
||||
</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
:disabled="step === 0 || step >= maxStep"
|
||||
@click="next"
|
||||
>
|
||||
下一步
|
||||
</button>
|
||||
<button class="btn" @click="reset">重置</button>
|
||||
<button class="btn" @click="copy(currentCmd)" :disabled="!currentCmd">
|
||||
<button
|
||||
class="btn"
|
||||
@click="reset"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
:disabled="!currentCmd"
|
||||
@click="copy(currentCmd)"
|
||||
>
|
||||
{{ copied ? '已复制' : '复制命令' }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="step > 0" class="progress">
|
||||
<div
|
||||
v-if="step > 0"
|
||||
class="progress"
|
||||
>
|
||||
Step {{ step }} / {{ maxStep }} · {{ steps[step - 1]?.title }}
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-title">角色</div>
|
||||
<div class="card-title">
|
||||
角色
|
||||
</div>
|
||||
<div class="role">
|
||||
<div class="pill">Client(你的应用)</div>
|
||||
<div class="pill">Authorization Server(微信/Google 等)</div>
|
||||
<div class="pill">Resource Server(你的 API)</div>
|
||||
<div class="pill">
|
||||
Client(你的应用)
|
||||
</div>
|
||||
<div class="pill">
|
||||
Authorization Server(微信/Google 等)
|
||||
</div>
|
||||
<div class="pill">
|
||||
Resource Server(你的 API)
|
||||
</div>
|
||||
</div>
|
||||
<div class="desc">
|
||||
OAuth2
|
||||
@@ -49,17 +81,30 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">本步要做什么</div>
|
||||
<div class="desc">{{ steps[step - 1]?.desc || '点击开始' }}</div>
|
||||
<div v-if="steps[step - 1]?.warn" class="warn">
|
||||
<div class="warn-title">注意</div>
|
||||
<div class="warn-text">{{ steps[step - 1]?.warn }}</div>
|
||||
<div class="card-title">
|
||||
本步要做什么
|
||||
</div>
|
||||
<div class="desc">
|
||||
{{ steps[step - 1]?.desc || '点击开始' }}
|
||||
</div>
|
||||
<div
|
||||
v-if="steps[step - 1]?.warn"
|
||||
class="warn"
|
||||
>
|
||||
<div class="warn-title">
|
||||
注意
|
||||
</div>
|
||||
<div class="warn-text">
|
||||
{{ steps[step - 1]?.warn }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">请求/命令示例(可照抄)</div>
|
||||
<div class="card-title">
|
||||
请求/命令示例(可照抄)
|
||||
</div>
|
||||
<pre
|
||||
class="code"
|
||||
><code>{{ currentCmd || '(点击开始后显示)' }}</code></pre>
|
||||
@@ -70,7 +115,9 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">你真正需要记住的 4 件事</div>
|
||||
<div class="card-title">
|
||||
你真正需要记住的 4 件事
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li>
|
||||
<strong>redirect_uri 必须白名单:</strong>避免被人把 code
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
<template>
|
||||
<div class="password-hashing-demo">
|
||||
<div class="header">
|
||||
<div class="title">🔐 密码存储:哈希 + 盐 + 慢</div>
|
||||
<div class="title">
|
||||
🔐 密码存储:哈希 + 盐 + 慢
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
演示 PBKDF2(模拟慢哈希)如何抵抗彩虹表/暴力破解;真实项目通常选
|
||||
bcrypt/Argon2。
|
||||
@@ -18,7 +20,9 @@
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-title">输入</div>
|
||||
<div class="card-title">
|
||||
输入
|
||||
</div>
|
||||
|
||||
<label class="label">密码</label>
|
||||
<input
|
||||
@@ -27,7 +31,7 @@
|
||||
class="input"
|
||||
placeholder="例如:123456"
|
||||
@input="debouncedRecompute"
|
||||
/>
|
||||
>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
@@ -42,29 +46,43 @@
|
||||
max="200000"
|
||||
step="1000"
|
||||
@input="debouncedRecompute"
|
||||
/>
|
||||
<div class="hint">越大越慢,暴力破解成本越高(但登录也更慢)。</div>
|
||||
>
|
||||
<div class="hint">
|
||||
越大越慢,暴力破解成本越高(但登录也更慢)。
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<label class="toggle">
|
||||
<input v-model="saltEnabled" type="checkbox" @change="recompute" />
|
||||
<input
|
||||
v-model="saltEnabled"
|
||||
type="checkbox"
|
||||
@change="recompute"
|
||||
>
|
||||
<span>启用盐(salt)</span>
|
||||
</label>
|
||||
<button class="btn" @click="regenSalt" :disabled="!saltEnabled">
|
||||
<button
|
||||
class="btn"
|
||||
:disabled="!saltEnabled"
|
||||
@click="regenSalt"
|
||||
>
|
||||
生成新盐
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mono-box">
|
||||
<div class="mono-label">salt</div>
|
||||
<div class="mono-label">
|
||||
salt
|
||||
</div>
|
||||
<code class="mono">{{ saltEnabled ? saltHex : '(disabled)' }}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">输出(模拟)</div>
|
||||
<div class="card-title">
|
||||
输出(模拟)
|
||||
</div>
|
||||
|
||||
<div class="status">
|
||||
<span class="badge">Algorithm: PBKDF2-SHA256</span>
|
||||
@@ -72,12 +90,16 @@
|
||||
</div>
|
||||
|
||||
<div class="mono-box">
|
||||
<div class="mono-label">derived key (hex)</div>
|
||||
<div class="mono-label">
|
||||
derived key (hex)
|
||||
</div>
|
||||
<code class="mono">{{ hashHex || '(请输入密码)' }}</code>
|
||||
</div>
|
||||
|
||||
<div class="alert">
|
||||
<div class="alert-title">结论</div>
|
||||
<div class="alert-title">
|
||||
结论
|
||||
</div>
|
||||
<div class="alert-text">
|
||||
不要存明文;不要用无盐的快速哈希(MD5/SHA1/SHA256 直接 hash 密码)。
|
||||
应使用“专门的密码哈希/KDF(慢 + 盐)”,并设置合理成本。
|
||||
@@ -92,15 +114,23 @@
|
||||
</div>
|
||||
<div class="two">
|
||||
<div class="mono-box">
|
||||
<div class="mono-label">salt A</div>
|
||||
<div class="mono-label">
|
||||
salt A
|
||||
</div>
|
||||
<code class="mono">{{ saltA }}</code>
|
||||
<div class="mono-label">hash A</div>
|
||||
<div class="mono-label">
|
||||
hash A
|
||||
</div>
|
||||
<code class="mono">{{ hashA || '-' }}</code>
|
||||
</div>
|
||||
<div class="mono-box">
|
||||
<div class="mono-label">salt B</div>
|
||||
<div class="mono-label">
|
||||
salt B
|
||||
</div>
|
||||
<code class="mono">{{ saltB }}</code>
|
||||
<div class="mono-label">hash B</div>
|
||||
<div class="mono-label">
|
||||
hash B
|
||||
</div>
|
||||
<code class="mono">{{ hashB || '-' }}</code>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,77 +5,148 @@
|
||||
<template>
|
||||
<div class="session-demo">
|
||||
<div class="header">
|
||||
<div class="title">🍪 Session + Cookie:有状态登录</div>
|
||||
<div class="title">
|
||||
🍪 Session + Cookie:有状态登录
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
默认手动推进:先看清楚状态再进入下一步(避免“自动下一步”误解)。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<button class="btn primary" @click="start" :disabled="step !== 0">
|
||||
开始
|
||||
</button>
|
||||
<button class="btn" @click="prev" :disabled="step <= 1">上一步</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
@click="next"
|
||||
:disabled="step !== 0"
|
||||
@click="start"
|
||||
>
|
||||
开始
|
||||
</button>
|
||||
<button
|
||||
class="btn"
|
||||
:disabled="step <= 1"
|
||||
@click="prev"
|
||||
>
|
||||
上一步
|
||||
</button>
|
||||
<button
|
||||
class="btn primary"
|
||||
:disabled="step === 0 || step >= maxStep"
|
||||
@click="next"
|
||||
>
|
||||
下一步
|
||||
</button>
|
||||
<button class="btn" @click="reset">重置</button>
|
||||
<button
|
||||
class="btn"
|
||||
@click="reset"
|
||||
>
|
||||
重置
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-if="step > 0" class="progress">
|
||||
<div
|
||||
v-if="step > 0"
|
||||
class="progress"
|
||||
>
|
||||
Step {{ step }} / {{ maxStep }} · {{ steps[step - 1]?.title }}
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-title">浏览器(客户端)</div>
|
||||
<div class="card-title">
|
||||
浏览器(客户端)
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-title">Cookie Jar</div>
|
||||
<div v-if="cookie" class="kv">
|
||||
<div class="k">session_id</div>
|
||||
<div class="v mono">{{ cookie }}</div>
|
||||
<div class="box-title">
|
||||
Cookie Jar
|
||||
</div>
|
||||
<div
|
||||
v-if="cookie"
|
||||
class="kv"
|
||||
>
|
||||
<div class="k">
|
||||
session_id
|
||||
</div>
|
||||
<div class="v mono">
|
||||
{{ cookie }}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="empty"
|
||||
>
|
||||
暂无 Cookie
|
||||
</div>
|
||||
<div v-else class="empty">暂无 Cookie</div>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-title">本步请求</div>
|
||||
<div class="box-title">
|
||||
本步请求
|
||||
</div>
|
||||
<pre class="code"><code>{{ clientRequest }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">服务器</div>
|
||||
<div class="card-title">
|
||||
服务器
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-title">Session Store(Redis/Memory)</div>
|
||||
<div v-if="session" class="kv">
|
||||
<div class="k mono">{{ cookie }}</div>
|
||||
<div class="box-title">
|
||||
Session Store(Redis/Memory)
|
||||
</div>
|
||||
<div
|
||||
v-if="session"
|
||||
class="kv"
|
||||
>
|
||||
<div class="k mono">
|
||||
{{ cookie }}
|
||||
</div>
|
||||
<div class="v">
|
||||
<div class="row"><span class="muted">user_id</span> 123</div>
|
||||
<div class="row"><span class="muted">username</span> alice</div>
|
||||
<div class="row"><span class="muted">role</span> admin</div>
|
||||
<div class="row">
|
||||
<span class="muted">user_id</span> 123
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="muted">username</span> alice
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="muted">role</span> admin
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="empty">暂无 Session</div>
|
||||
<div
|
||||
v-else
|
||||
class="empty"
|
||||
>
|
||||
暂无 Session
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-title">本步响应</div>
|
||||
<div class="box-title">
|
||||
本步响应
|
||||
</div>
|
||||
<pre class="code"><code>{{ serverResponse }}</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">{{ steps[step - 1]?.title || '流程说明' }}</div>
|
||||
<div class="desc">{{ steps[step - 1]?.desc }}</div>
|
||||
<div v-if="steps[step - 1]?.warn" class="warn">
|
||||
<div class="warn-title">注意</div>
|
||||
<div class="warn-text">{{ steps[step - 1]?.warn }}</div>
|
||||
<div class="card-title">
|
||||
{{ steps[step - 1]?.title || '流程说明' }}
|
||||
</div>
|
||||
<div class="desc">
|
||||
{{ steps[step - 1]?.desc }}
|
||||
</div>
|
||||
<div
|
||||
v-if="steps[step - 1]?.warn"
|
||||
class="warn"
|
||||
>
|
||||
<div class="warn-title">
|
||||
注意
|
||||
</div>
|
||||
<div class="warn-text">
|
||||
{{ steps[step - 1]?.warn }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
<template>
|
||||
<div class="session-vs-jwt-demo">
|
||||
<div class="header">
|
||||
<div class="title">🧩 Session vs JWT:怎么选?</div>
|
||||
<div class="title">
|
||||
🧩 Session vs JWT:怎么选?
|
||||
</div>
|
||||
<div class="subtitle">
|
||||
选你的约束条件,得到推荐方案(并解释原因)。这比“背结论”更好用。
|
||||
</div>
|
||||
@@ -13,7 +15,9 @@
|
||||
|
||||
<div class="grid">
|
||||
<div class="card">
|
||||
<div class="card-title">你的场景</div>
|
||||
<div class="card-title">
|
||||
你的场景
|
||||
</div>
|
||||
|
||||
<label class="label">主要客户端</label>
|
||||
<div class="row">
|
||||
@@ -96,30 +100,52 @@
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">推荐</div>
|
||||
<div class="card-title">
|
||||
推荐
|
||||
</div>
|
||||
<div class="recommend">
|
||||
<div class="pill primary">{{ recommendation.title }}</div>
|
||||
<div class="desc">{{ recommendation.desc }}</div>
|
||||
<div class="pill primary">
|
||||
{{ recommendation.title }}
|
||||
</div>
|
||||
<div class="desc">
|
||||
{{ recommendation.desc }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-title">为什么</div>
|
||||
<div class="box-title">
|
||||
为什么
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li v-for="(x, i) in recommendation.reasons" :key="i">{{ x }}</li>
|
||||
<li
|
||||
v-for="(x, i) in recommendation.reasons"
|
||||
:key="i"
|
||||
>
|
||||
{{ x }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="box">
|
||||
<div class="box-title">落地建议</div>
|
||||
<div class="box-title">
|
||||
落地建议
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li v-for="(x, i) in recommendation.tips" :key="i">{{ x }}</li>
|
||||
<li
|
||||
v-for="(x, i) in recommendation.tips"
|
||||
:key="i"
|
||||
>
|
||||
{{ x }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-title">常见误区</div>
|
||||
<div class="card-title">
|
||||
常见误区
|
||||
</div>
|
||||
<ul class="list">
|
||||
<li>
|
||||
<strong>JWT ≠ 更安全:</strong>JWT
|
||||
|
||||
Reference in New Issue
Block a user