天津九隆天一别墅原木定制工厂 發表於 2026-1-14 09:38:00

🔥面试官:说说看,用户登录后拿到的 Token,你应该怎么存?存哪里?

<h1 data-id="heading-0">🧑‍💻 写在开头</h1>
<p>点赞 + 收藏 === 学会🤣🤣🤣</p>
<div>
<div>
<h3 data-id="heading-0">开篇:一个经典的面试题</h3>
<p>“说说看,用户登录后拿到的 Token,前端应该怎么存?”</p>
<p>这个问题看似简单,却能清晰地分辨出一个前端开发者对安全的理解深度。是存到 <code>localStorage</code>?<code>sessionStorage</code>?还是 <code>Cookie</code>?又或者是内存里?不同的选择背后,是截然不同的安全考量。</p>
<p>今天,来聊一聊 Token 的存储之道,让你不仅知道怎么做,更明白为什么这么做。</p>
</div>
</div>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260114093624186-1713282943.png" alt="ScreenShot_2026-01-14_093508_484" loading="lazy"></p>
<h3 data-id="heading-1">选项一:Web Storage(<code>localStorage</code>&nbsp;/&nbsp;<code>sessionStorage</code>)</h3>
<p>这是最直观、最容易想到的方案。</p>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 登录成功后
const token = 'your_jwt_token_here';
localStorage.setItem('auth_token', token);

// 后续请求时带上
axios.interceptors.request.use((config) =&gt; {
const token = localStorage.getItem('auth_token');
if (token) {
    config.headers.Authorization = `Bearer ${token}`;
}
return config;
});</pre>
</div>
<div>
<div>
<p><strong>优点:</strong></p>
<ul>
<li><strong>简单易用</strong>:API 简单,上手快。</li>
<li><strong>永久存储</strong>(<code>localStorage</code>):除非手动清除,否则一直存在。</li>
<li><strong>会话期存储</strong>(<code>sessionStorage</code>):页面关闭即清除,更安全一点。</li>
</ul>
<p><strong>致命缺点:</strong></p>
<ul>
<li><strong>极易受到 XSS (跨站脚本攻击) 的攻击</strong>。这是最核心的问题。如果网站存在 XSS 漏洞,攻击者的恶意脚本可以轻易地读取到 <code>localStorage</code> 中的所有 Token,从而窃取用户身份。</li>
</ul>
<p><strong>结论:</strong> <strong>不推荐,尤其对于敏感应用。</strong> 除非你的应用完全不存在 XSS 风险(但这几乎不可能),或者 Token 的安全级别要求不高。</p>
<h3 data-id="heading-2">选项二:Cookie</h3>
<p>Cookie 是传统且常见的身份验证载体。</p>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">// 服务端设置 Cookie (HTTP Response Header)
Set-Cookie: auth_token=your_jwt_token_here; Path=/; HttpOnly; Secure

// 前端无需特殊处理,浏览器会自动在每次请求中携带</pre>
</div>
<div>
<div>
<p>注意这里的两个关键属性:</p>
<ul>
<li><code>HttpOnly</code>:<strong>这是对抗 XSS 的神器</strong>。设置了 <code>HttpOnly</code> 的 Cookie 无法通过 JavaScript 的 <code>document.cookie</code> API 访问,这意味着即使发生 XSS,攻击者也无法窃取到 Token。</li>
<li><code>Secure</code>:强制 Cookie 只能在 HTTPS 协议下被发送,防止在网络传输中被窃听。</li>
</ul>
<p><strong>优点:</strong></p>
<ul>
<li><strong>免疫 XSS</strong>(得益于 <code>HttpOnly</code>):无法通过 JS 读取,安全性高。</li>
<li><strong>可控制生命周期</strong>:通过 <code>Expires</code> 或 <code>Max-Age</code> 设置过期时间。</li>
<li><strong>自动管理</strong>:浏览器自动在同源请求中携带。</li>
</ul>
<p><strong>缺点:</strong></p>
<ul>
<li><strong>容易受到 CSRF (跨站请求伪造) 的攻击</strong>。因为浏览器会自动在请求中带上 Cookie,攻击者可以诱导用户点击一个链接,从而以用户的身份发起恶意请求。</li>
<li><strong>需要额外的 CSRF 防护措施</strong>,如使用 Anti-CSRF Token、验证 Origin/Referer Header 等。</li>
</ul>
<p><strong>结论:</strong> <strong>是一个可行的方案,但必须配套完善的 CSRF 防御机制。</strong></p>
<h3 data-id="heading-3">选项三:内存(Memory)</h3>
<p>将 Token 保存在 JavaScript 变量中。</p>
</div>
</div>
</div>
<div class="cnblogs_Highlighter">
<pre class="brush:csharp;gutter:true;">let inMemoryToken = null;

// 登录成功后
const login = async () =&gt; {
const response = await loginAPI(username, password);
inMemoryToken = response.data.token; // 存到内存变量
};

// 请求拦截器中添加
axios.interceptors.request.use((config) =&gt; {
if (inMemoryToken) {
    config.headers.Authorization = `Bearer ${inMemoryToken}`;
}
return config;
});

// 退出登录或页面刷新时,Token 消失</pre>
</div>
<div>
<div>
<p><strong>优点:</strong></p>
<ul>
<li><strong>安全性极高</strong>:由于 Token 只存在于当前页面的内存中,关闭页面或刷新页面后 Token 立即消失。XSS 攻击者很难通过一次性注入的脚本持续地窃取到 Token(除非攻击代码常驻内存)。</li>
</ul>
<p><strong>缺点:</strong></p>
<ul>
<li><strong>体验差</strong>:页面一旦刷新,Token 就没了,用户需要重新登录。这对于单页面应用 (SPA) 来说是致命的。</li>
</ul>
<p><strong>结论:</strong> <strong>适用于安全要求极高、不介意频繁登录的场景(如银行系统)。</strong> 对于普通 Web 应用,体验不可接受。</p>
<hr>
<h3 data-id="heading-4">终极方案:组合拳 + 架构思维</h3>
<p>既然没有完美的银弹,现代前端的最佳实践通常是 <strong>组合方案</strong> 和 <strong>架构优化</strong>。</p>
<h4 data-id="heading-5">实践一:Cookie(HttpOnly + Secure) + 防御 CSRF</h4>
<p>这是最传统但依然非常稳健的方案。</p>
<ol>
<li><strong>后端</strong>在登录成功后,设置一个 <code>HttpOnly</code>、<code>Secure</code> 的 Cookie 来存放 Token(可以是 JWT,也可以是 Session ID)。</li>
<li><strong>前端</strong>基本不用操心 Token 的存储和携带问题,由浏览器自动完成。</li>
<li><strong>后端</strong>必须部署完善的 <strong>CSRF 防护策略</strong>,例如:
<ul>
<li>从 Cookie 中读取 Token 或 Session ID 进行身份验证。</li>
<li>同时,要求请求必须携带一个额外的(由后端生成并通过 API 返回给前端的)<code>X-CSRF-Token</code> Header,并与 Session 中存储的值进行比对。</li>
</ul>
</li>
</ol>
<h4 data-id="heading-6">实践二:Access Token + Refresh Token</h4>
<p>这是目前 API 接口认证非常流行的方案,完美解决了内存存储体验差的问题。</p>
<ol>
<li><strong>登录</strong>:用户输入密码登录。</li>
<li><strong>返回双 Token</strong>:服务端返回两个 Token:
<ul>
<li><code>access_token</code>:短期有效(例如 2 小时),用于请求受保护的 API。</li>
<li><code>refresh_token</code>:长期有效(例如 7 天),仅用于获取新的 <code>access_token</code>,不应具备API访问权限。</li>
</ul>
</li>
<li><strong>存储策略</strong>:
<ul>
<li><strong><code>access_token</code></strong>:<strong>存入内存</strong>。这样即使被 XSS 窃取,有效期也很短,风险可控。</li>
<li><strong><code>refresh_token</code></strong>:<strong>存入 <code>HttpOnly</code> Cookie</strong>。因为它有效期长,必须严加保护。由于其本身不直接用于业务请求,即使遭遇 CSRF,攻击者也无法用它来做任何关键操作(只能用来换一个短期的 <code>access_token</code>,而该 <code>access_token</code> 又会因为存在于内存而难以被攻击者获取)。</li>
</ul>
</li>
<li><strong>无感刷新</strong>:当 <code>access_token</code> 过期后,前端自动(通过 <code>refresh_token</code>)调用刷新接口获取新的 <code>access_token</code>,用户无感知。</li>
</ol>
<p><strong>这个方案在安全性和用户体验之间取得了绝佳的平衡,是当前众多大型应用的首选。</strong></p>
</div>
<h3 data-id="heading-7">总结与建议</h3>
<p><img src="https://img2024.cnblogs.com/blog/2149129/202601/2149129-20260114093738098-987318699.png" alt="ScreenShot_2026-01-14_093522_079" loading="lazy"></p>
<div>
<div>
<p><strong>给你的最终建议:</strong></p>
<ul>
<li><strong>如果你在做的是一个简单的、内部使用的、对安全性要求不高的工具</strong>,用 <code>localStorage</code> 图个方便也无可厚非,但要清楚风险。</li>
<li><strong>如果你在做的是一个传统的、服务端渲染的多页应用</strong>,使用 <code>HttpOnly</code> Cookie 并配套 CSRF 防护是标准做法。</li>
<li><strong>如果你在做的是一个现代化的 SPA(如 React/Vue 应用)</strong>,强烈推荐研究并采用 <strong>Access Token (内存) + Refresh Token (<code>HttpOnly</code> Cookie)</strong> 的方案。</li>
</ul>
<p>安全没有绝对,只有相对的权衡。理解每种方案的利弊,并根据你的实际业务场景做出最适合的选择,才是最重要的。</p>
</div>
<div>
<h3 id="tid-D8HBxE">如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。</h3>
</div>
<p><em><img src="https://img2024.cnblogs.com/blog/2149129/202501/2149129-20250122165814748-630765389.png" alt="" loading="lazy"></em></p>
</div>
</div><br><br>
来源:https://www.cnblogs.com/smileZAZ/p/19480348
頁: [1]
查看完整版本: 🔥面试官:说说看,用户登录后拿到的 Token,你应该怎么存?存哪里?