普通视图

发现新文章,点击刷新页面。
昨天以前首页

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

2025年9月2日 10:39

开篇:一个经典的面试题

“说说看,用户登录后拿到的 Token,前端应该怎么存?”

这个问题看似简单,却能清晰地分辨出一个前端开发者对安全的理解深度。是存到 localStoragesessionStorage?还是 Cookie?又或者是内存里?不同的选择背后,是截然不同的安全考量。

今天,来聊一聊 Token 的存储之道,让你不仅知道怎么做,更明白为什么这么做。

a007f1e0ly1i4uoqtydizj20k60k6tav.jpg

选项一:Web Storage(localStorage / sessionStorage

这是最直观、最容易想到的方案。

// 登录成功后
const token = 'your_jwt_token_here';
localStorage.setItem('auth_token', token);

// 后续请求时带上
axios.interceptors.request.use((config) => {
  const token = localStorage.getItem('auth_token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

优点:

  • 简单易用:API 简单,上手快。
  • 永久存储localStorage):除非手动清除,否则一直存在。
  • 会话期存储sessionStorage):页面关闭即清除,更安全一点。

致命缺点:

  • 极易受到 XSS (跨站脚本攻击) 的攻击。这是最核心的问题。如果网站存在 XSS 漏洞,攻击者的恶意脚本可以轻易地读取到 localStorage 中的所有 Token,从而窃取用户身份。

结论: 不推荐,尤其对于敏感应用。 除非你的应用完全不存在 XSS 风险(但这几乎不可能),或者 Token 的安全级别要求不高。

选项二:Cookie

Cookie 是传统且常见的身份验证载体。

// 服务端设置 Cookie (HTTP Response Header)
Set-Cookie: auth_token=your_jwt_token_here; Path=/; HttpOnly; Secure

// 前端无需特殊处理,浏览器会自动在每次请求中携带

注意这里的两个关键属性:

  • HttpOnly这是对抗 XSS 的神器。设置了 HttpOnly 的 Cookie 无法通过 JavaScript 的 document.cookie API 访问,这意味着即使发生 XSS,攻击者也无法窃取到 Token。
  • Secure:强制 Cookie 只能在 HTTPS 协议下被发送,防止在网络传输中被窃听。

优点:

  • 免疫 XSS(得益于 HttpOnly):无法通过 JS 读取,安全性高。
  • 可控制生命周期:通过 ExpiresMax-Age 设置过期时间。
  • 自动管理:浏览器自动在同源请求中携带。

缺点:

  • 容易受到 CSRF (跨站请求伪造) 的攻击。因为浏览器会自动在请求中带上 Cookie,攻击者可以诱导用户点击一个链接,从而以用户的身份发起恶意请求。
  • 需要额外的 CSRF 防护措施,如使用 Anti-CSRF Token、验证 Origin/Referer Header 等。

结论: 是一个可行的方案,但必须配套完善的 CSRF 防御机制。

选项三:内存(Memory)

将 Token 保存在 JavaScript 变量中。

let inMemoryToken = null;

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

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

// 退出登录或页面刷新时,Token 消失

优点:

  • 安全性极高:由于 Token 只存在于当前页面的内存中,关闭页面或刷新页面后 Token 立即消失。XSS 攻击者很难通过一次性注入的脚本持续地窃取到 Token(除非攻击代码常驻内存)。

缺点:

  • 体验差:页面一旦刷新,Token 就没了,用户需要重新登录。这对于单页面应用 (SPA) 来说是致命的。

结论: 适用于安全要求极高、不介意频繁登录的场景(如银行系统)。 对于普通 Web 应用,体验不可接受。


终极方案:组合拳 + 架构思维

既然没有完美的银弹,现代前端的最佳实践通常是 组合方案架构优化

实践一:Cookie(HttpOnly + Secure) + 防御 CSRF

这是最传统但依然非常稳健的方案。

  1. 后端在登录成功后,设置一个 HttpOnlySecure 的 Cookie 来存放 Token(可以是 JWT,也可以是 Session ID)。
  2. 前端基本不用操心 Token 的存储和携带问题,由浏览器自动完成。
  3. 后端必须部署完善的 CSRF 防护策略,例如:
    • 从 Cookie 中读取 Token 或 Session ID 进行身份验证。
    • 同时,要求请求必须携带一个额外的(由后端生成并通过 API 返回给前端的)X-CSRF-Token Header,并与 Session 中存储的值进行比对。

实践二:Access Token + Refresh Token

这是目前 API 接口认证非常流行的方案,完美解决了内存存储体验差的问题。

  1. 登录:用户输入密码登录。
  2. 返回双 Token:服务端返回两个 Token:
    • access_token:短期有效(例如 2 小时),用于请求受保护的 API。
    • refresh_token:长期有效(例如 7 天),仅用于获取新的 access_token,不应具备API访问权限。
  3. 存储策略
    • access_token存入内存。这样即使被 XSS 窃取,有效期也很短,风险可控。
    • refresh_token存入 HttpOnly Cookie。因为它有效期长,必须严加保护。由于其本身不直接用于业务请求,即使遭遇 CSRF,攻击者也无法用它来做任何关键操作(只能用来换一个短期的 access_token,而该 access_token 又会因为存在于内存而难以被攻击者获取)。
  4. 无感刷新:当 access_token 过期后,前端自动(通过 refresh_token)调用刷新接口获取新的 access_token,用户无感知。

这个方案在安全性和用户体验之间取得了绝佳的平衡,是当前众多大型应用的首选。


总结与建议

存储位置 优点 缺点 适用场景
Web Storage 简单易用 极易被 XSS 不推荐用于存敏感 Token
Cookie 可防 XSS (HttpOnly) 需防御 CSRF 传统 Web 应用(需配 CSRF 防御)
内存 安全性极高 页面刷新即失效 安全至上、不介意体验的场景
组合策略 安全与体验的平衡 架构稍复杂 现代 Web 应用最佳实践

给你的最终建议:

  • 如果你在做的是一个简单的、内部使用的、对安全性要求不高的工具,用 localStorage 图个方便也无可厚非,但要清楚风险。
  • 如果你在做的是一个传统的、服务端渲染的多页应用,使用 HttpOnly Cookie 并配套 CSRF 防护是标准做法。
  • 如果你在做的是一个现代化的 SPA(如 React/Vue 应用),强烈推荐研究并采用 Access Token (内存) + Refresh Token (HttpOnly Cookie) 的方案。

安全没有绝对,只有相对的权衡。理解每种方案的利弊,并根据你的实际业务场景做出最适合的选择,才是最重要的。

下次再见!🌈

Snipaste_2025-04-27_15-18-02.png

❌
❌