JWT 应该存哪儿?5 种方式全面解析,选对方式很关键!

在 Web 场景里,最常见的几种**JWT(JSON Web Token)**存储方式主要有:

  1. Cookie(HttpOnly + Secure)
  2. 普通 Cookie(可被 JS 读取)
  3. localStorage
  4. sessionStorage
  5. 内存变量(in-memory)

下面从安全性、跨域便利性、实现复杂度等方面做一个详细对比,帮助你根据业务场景做出选择。


1. Cookie(HttpOnly + Secure)

1.1 基本原理

  • 服务器端在用户登录或获取 Token 后,通过Set-Cookie响应头把 Token 设置到浏览器 Cookie 中,并加上HttpOnlySecureSameSite等安全属性。
  • 浏览器在后续请求时自动携带 Cookie,无需手动在前端写逻辑附带 Token。
  • HttpOnly 可以防止前端脚本直接读取 Cookie,从而降低 XSS 造成令牌泄露的风险。

1.2 优点

  1. 安全性高

    • HttpOnly 减少 XSS 攻击时泄露 Token 的风险;
    • Secure 确保只能在 HTTPS 下传输 Token,防止明文窃听;
    • 可结合SameSite、CSRF Token 等策略提升对 CSRF 攻击的防御能力。
  2. 使用方便

    • 浏览器请求会自动携带 Cookie,后端可直接从请求的 Cookie 中获取 Token 并校验。
  3. 更贴近传统 Session 的使用习惯

    • 对于传统后端渲染或同域名场景,迁移成本低。

1.3 缺点

  1. 跨域场景配置相对复杂

    • 如果需要前后端在不同域名(或端口)下交互,就要在服务器端正确配置 CORS(跨域资源共享)、Access-Control-Allow-Credentials等,且 Cookie 的SameSite可能需要设为None并配合Secure
  2. 需要额外防范 CSRF

    • 当浏览器自动带 Cookie 时,攻击者若能诱导用户访问恶意链接,也会携带 Cookie,因此需要在后端或前端做 CSRF 防御(常见方式:CSRF Token / Double Submit Cookie / SameSite 策略)。

1.4 适用场景

  • 同域或同顶级域名下的前后端应用(SPA、SSR、微前端等)。
  • 需要较高安全性、不希望前端脚本访问到 Token。
  • 可以配合 CSRF 方案且对跨域配置有一定理解。

2. 普通 Cookie(可被 JS 读取)

2.1 基本原理

  • 与上面类似,将 Token 存在浏览器 Cookie 中,但不加HttpOnly属性,所以前端脚本(JS)可以读取和写入 Token。

2.2 优点

  1. 实现简单

    • 不用在请求头里手动加 Token,浏览器会自动带上;同时若前端要展示或处理与 Token 关联的信息,也能直接读取 Cookie。

2.3 缺点

  1. 安全风险高

    • 任何前端脚本(包括被 XSS 注入的恶意脚本)都能读取到 Token,一旦泄露 Token,攻击者可冒充用户发起请求。
  2. 同样存在 CSRF 风险

    • 与 HttpOnly Cookie 一样,也需要做防范 CSRF 的工作。

2.4 适用场景

  • 原则上不推荐在生产环境使用,因为 XSS 一旦发生,Token 会立即泄露。
  • 可能仅在非常简单的 Demo 或内部工具里用到,但要非常注意 XSS 防护。

3. localStorage

3.1 基本原理

  • 前端在用户登录成功后,从后端获取 JWT,将其保存在浏览器的window.localStorage中。
  • 发送请求时,前端显式地从localStorage中拿 Token,加到Authorization: Bearer <token>请求头里。

3.2 优点

  1. 跨域、跨子域场景下更灵活

    • 不用费心 Cookie domain、SameSite、CORS Credentials 配置等,直接在请求头里带 Token。
  2. Token 管理可控

    • 前端能随时读写 Token,或根据业务需要做一些缓存、刷新逻辑。

3.3 缺点

  1. 易受 XSS 攻击

    • 若站点存在 XSS 漏洞,恶意脚本可直接访问到localStorage内容,将 Token 盗走。
  2. 需要自行处理 CSRF

    • 虽然通过Authorization头可以避免自动携带 Cookie 的情况,但如果你有其他需求(比如还在用 Cookie 维护部分状态)仍有可能受 CSRF 影响。
  3. 实现稍复杂

    • 每次请求需要手动添加Authorization头。

3.4 适用场景

  • 前后端完全分离,且经常需要跨域;
  • Token 主要用于 API 授权,且配合充分的 XSS 防护(比如严格的 CSP、输入校验、第三方脚本管理等)。

4. sessionStorage

4.1 基本原理

  • 与 localStorage 类似,也是前端脚本可读,但生命周期是浏览器会话级(Tab / 窗口关闭即失效)。

4.2 优点

  1. 生命周期更短

    • 关闭浏览器标签或窗口后 Token 即清除,减少长期存在的安全风险。
  2. 实现上与 localStorage 相似

    • 手动在请求头中附带 Token。

4.3 缺点

  1. 仍然易受 XSS 攻击

    • 只要脚本能运行,就能读写 sessionStorage 中的 Token。
  2. 无法跨标签共享

    • 如果用户需要在多个浏览器标签间切换,则每个 Tab 都需要重新登录获取 Token(除非有更复杂的处理逻辑)。

4.4 适用场景

  • 希望 Token 在浏览器最小化时限内存在;
  • 不希望 Token 在浏览器不同 Tab 间共享或在刷新页面后依然保留。

5. 内存变量(In-Memory)

5.1 基本原理

  • 前端在登录后,只保存在运行时内存变量中(例如 React 或 Vue 的全局状态、JS 变量),不写入 Cookie 或 Web 存储。
  • 在页面刷新或关闭时,该变量会丢失,需要依赖其他机制(刷新 Token 或重新登录)。

5.2 优点

  1. 无法直接被持久化

    • 相对减少在磁盘或浏览器持久存储中被窃取的风险。
    • 部分攻击向量(如直接读取 localStorage 或 Cookie)失效。
  2. 灵活性

    • 适合非常短生命周期的应用,或者对安全要求极高,希望最大程度减少 Token 暴露。

5.3 缺点

  1. 页面刷新 / 重载后丢失状态

    • 用户体验上会有不便,需要重新获取 Token;
    • 需要额外的无感刷新或"Silent Refresh"逻辑(常与 Refresh Token 结合)。
  2. 依旧易受 XSS 攻击

    • 若脚本能执行,攻击者也可以获取到当前内存里的 Token(不过获取时机相对更苛刻)。

5.4 适用场景

  • 安全敏感度高希望极短期持有 Token(比如只在单页面操作时保持登录,刷新就要重新获取),可结合刷新 Token 机制。
  • 前端要有丰富的无感刷新重登录策略,否则会频繁打断用户体验。

6. 总体对比

方式 安全性(防XSS) 防CSRF 跨域/多域配置 易用性/集成难度 生命周期管理
Cookie(HttpOnly) 较高(HttpOnly) 需要配合 CSRF 方案 需要 CORS + SameSite 配置 易用(自动发送),但配置跨域略复杂 由服务端/Cookie 属性控制
普通 Cookie 较低(可读写) 同上,需CSRF防范 同上 简单,但易泄露 同上
localStorage 较低 通常不自动发起CSRF,但仍需整体防范 较灵活 实现需要前端手动传 Token 前端自行控制,需刷新策略
sessionStorage 同 localStorage 同 localStorage 同 localStorage 同 localStorage 关闭Tab即失效
内存变量(in-memory) 低(可读写) 无Cookie自动发送 最灵活 需自行实现刷新逻辑 刷新或关闭页面即失效

注:

  • 防XSS 并不是指"绝对防",而是指"难度高低"。HttpOnly Cookie 在前端脚本层面更难被直接读取,但如果 XSS 能执行任意脚本,也可能在发送请求时自动携带 Cookie 的情况下做进一步操作。
  • 防CSRF 主要关注的是浏览器是否自动带上凭证。Cookie(不管是否 HttpOnly)都会自动带上,需要后端或前端实现额外防护。
  • localStorage / sessionStorage / 内存变量 主要在发起请求时手动注入 Token,因此在默认情况下对 CSRF 攻击更有"天然屏障"(因为攻击者无法自动借助用户浏览器带上 Authorization 头),但若应用还有其他 Cookie-based 功能也需要防范 CSRF。

7. 如何选择?

  1. 同域名 / 安全要求较高 / 有后端渲染或 SPA 场景

    • 首选:HttpOnly Cookie+ CSRF 防护
    • 好处:自动带 Cookie,后端对请求身份校验统一,且 Token 不会被 JS 直接读取。
  2. 前后端分离 + 跨域复杂 + 需要细粒度控制 + 需要移动端/第三方对接

    • 可以使用localStorage内存变量 来管理 Token,并手动在请求中附加Authorization: Bearer <token>头,后端只需在每次请求解析该头即可。
    • 需要加强 XSS 防护策略(Content Security Policy、及时更新依赖、代码审计等)。
  3. 对用户体验要求特别高,且 Token 需随时刷新

    • 可以采用内存变量 + Refresh Token模式,在刷新或重新打开页面时进行"无感刷新"获取新 Token。适当平衡安全与体验。
  4. 普通 Cookie(可读写)

    • 并不建议在生产环境中使用,因为极易被 XSS 窃取。

8. 结论

  • 最安全的通用做法:把 JWT 放在 HttpOnly + Secure Cookie 中,配合 CSRF 防护和 HTTPS,全流程加密传输,阻断常见攻击手段。
  • 跨域更灵活的做法 :使用 localStorage(或 sessionStorage/内存变量)并在每次请求中手动加Authorization头,但要确保站点足够安全,尤其防范 XSS。
  • 选择依据 :主要看你对"安全 "和"跨域可行性"的权衡,也要结合自身的技术栈、业务复杂度和用户体验要求来做决定。

希望以上对比能帮助你更好地理解和选择合适的 Token 存储方式。若应用对安全要求很高,优先考虑 HttpOnly Cookie 方案 ;若需要同时支持多域名访问或移动端/第三方,则要结合 Token + localStorage/内存,并实施严格的前端安全策略。

相关推荐
打野赵怀真11 分钟前
React Hooks 的优势和使用场景
前端·javascript
HaushoLin15 分钟前
ERR_PNPM_DLX_NO_BIN No binaries found in tailwindcss
前端·vue.js·css3·html5
Lafar16 分钟前
Widget 树和 Element 树和RenderObject树是一一 对应的吗
前端
小桥风满袖17 分钟前
炸裂,前端神级动效库合集
前端·css
匆叔18 分钟前
Tauri 桌面端开发
前端·vue.js
1_2_3_18 分钟前
react-antd-column-resize(让你的table列可以拖拽列宽)
前端
Lafar19 分钟前
Flutter和iOS混合开发
前端·面试
九龙湖兔兔19 分钟前
pnpm给插件(naiveUI)打补丁
前端·架构
知心宝贝20 分钟前
【Nest.js 通关秘籍 - 基础篇】带你轻松掌握后端开发
前端·javascript·架构