在 Web 场景里,最常见的几种**JWT(JSON Web Token)**存储方式主要有:
- Cookie(HttpOnly + Secure)
- 普通 Cookie(可被 JS 读取)
- localStorage
- sessionStorage
- 内存变量(in-memory)
下面从安全性、跨域便利性、实现复杂度等方面做一个详细对比,帮助你根据业务场景做出选择。
1. Cookie(HttpOnly + Secure)
1.1 基本原理
- 服务器端在用户登录或获取 Token 后,通过
Set-Cookie
响应头把 Token 设置到浏览器 Cookie 中,并加上HttpOnly
、Secure
、SameSite
等安全属性。 - 浏览器在后续请求时自动携带 Cookie,无需手动在前端写逻辑附带 Token。
- HttpOnly 可以防止前端脚本直接读取 Cookie,从而降低 XSS 造成令牌泄露的风险。
1.2 优点
-
安全性高:
- HttpOnly 减少 XSS 攻击时泄露 Token 的风险;
- Secure 确保只能在 HTTPS 下传输 Token,防止明文窃听;
- 可结合
SameSite
、CSRF Token 等策略提升对 CSRF 攻击的防御能力。
-
使用方便:
- 浏览器请求会自动携带 Cookie,后端可直接从请求的 Cookie 中获取 Token 并校验。
-
更贴近传统 Session 的使用习惯:
- 对于传统后端渲染或同域名场景,迁移成本低。
1.3 缺点
-
跨域场景配置相对复杂:
- 如果需要前后端在不同域名(或端口)下交互,就要在服务器端正确配置 CORS(跨域资源共享)、
Access-Control-Allow-Credentials
等,且 Cookie 的SameSite
可能需要设为None
并配合Secure
。
- 如果需要前后端在不同域名(或端口)下交互,就要在服务器端正确配置 CORS(跨域资源共享)、
-
需要额外防范 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 优点
-
实现简单:
- 不用在请求头里手动加 Token,浏览器会自动带上;同时若前端要展示或处理与 Token 关联的信息,也能直接读取 Cookie。
2.3 缺点
-
安全风险高:
- 任何前端脚本(包括被 XSS 注入的恶意脚本)都能读取到 Token,一旦泄露 Token,攻击者可冒充用户发起请求。
-
同样存在 CSRF 风险:
- 与 HttpOnly Cookie 一样,也需要做防范 CSRF 的工作。
2.4 适用场景
- 原则上不推荐在生产环境使用,因为 XSS 一旦发生,Token 会立即泄露。
- 可能仅在非常简单的 Demo 或内部工具里用到,但要非常注意 XSS 防护。
3. localStorage
3.1 基本原理
- 前端在用户登录成功后,从后端获取 JWT,将其保存在浏览器的
window.localStorage
中。 - 发送请求时,前端显式地从
localStorage
中拿 Token,加到Authorization: Bearer <token>
请求头里。
3.2 优点
-
跨域、跨子域场景下更灵活:
- 不用费心 Cookie domain、SameSite、CORS Credentials 配置等,直接在请求头里带 Token。
-
Token 管理可控:
- 前端能随时读写 Token,或根据业务需要做一些缓存、刷新逻辑。
3.3 缺点
-
易受 XSS 攻击:
- 若站点存在 XSS 漏洞,恶意脚本可直接访问到
localStorage
内容,将 Token 盗走。
- 若站点存在 XSS 漏洞,恶意脚本可直接访问到
-
需要自行处理 CSRF:
- 虽然通过
Authorization
头可以避免自动携带 Cookie 的情况,但如果你有其他需求(比如还在用 Cookie 维护部分状态)仍有可能受 CSRF 影响。
- 虽然通过
-
实现稍复杂:
- 每次请求需要手动添加
Authorization
头。
- 每次请求需要手动添加
3.4 适用场景
- 前后端完全分离,且经常需要跨域;
- Token 主要用于 API 授权,且配合充分的 XSS 防护(比如严格的 CSP、输入校验、第三方脚本管理等)。
4. sessionStorage
4.1 基本原理
- 与 localStorage 类似,也是前端脚本可读,但生命周期是浏览器会话级(Tab / 窗口关闭即失效)。
4.2 优点
-
生命周期更短:
- 关闭浏览器标签或窗口后 Token 即清除,减少长期存在的安全风险。
-
实现上与 localStorage 相似:
- 手动在请求头中附带 Token。
4.3 缺点
-
仍然易受 XSS 攻击:
- 只要脚本能运行,就能读写 sessionStorage 中的 Token。
-
无法跨标签共享:
- 如果用户需要在多个浏览器标签间切换,则每个 Tab 都需要重新登录获取 Token(除非有更复杂的处理逻辑)。
4.4 适用场景
- 希望 Token 在浏览器最小化时限内存在;
- 不希望 Token 在浏览器不同 Tab 间共享或在刷新页面后依然保留。
5. 内存变量(In-Memory)
5.1 基本原理
- 前端在登录后,只保存在运行时内存变量中(例如 React 或 Vue 的全局状态、JS 变量),不写入 Cookie 或 Web 存储。
- 在页面刷新或关闭时,该变量会丢失,需要依赖其他机制(刷新 Token 或重新登录)。
5.2 优点
-
无法直接被持久化:
- 相对减少在磁盘或浏览器持久存储中被窃取的风险。
- 部分攻击向量(如直接读取 localStorage 或 Cookie)失效。
-
灵活性:
- 适合非常短生命周期的应用,或者对安全要求极高,希望最大程度减少 Token 暴露。
5.3 缺点
-
页面刷新 / 重载后丢失状态:
- 用户体验上会有不便,需要重新获取 Token;
- 需要额外的无感刷新或"Silent Refresh"逻辑(常与 Refresh Token 结合)。
-
依旧易受 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. 如何选择?
-
同域名 / 安全要求较高 / 有后端渲染或 SPA 场景
- 首选:HttpOnly Cookie+ CSRF 防护
- 好处:自动带 Cookie,后端对请求身份校验统一,且 Token 不会被 JS 直接读取。
-
前后端分离 + 跨域复杂 + 需要细粒度控制 + 需要移动端/第三方对接
- 可以使用localStorage 或内存变量 来管理 Token,并手动在请求中附加
Authorization: Bearer <token>
头,后端只需在每次请求解析该头即可。 - 需要加强 XSS 防护策略(Content Security Policy、及时更新依赖、代码审计等)。
- 可以使用localStorage 或内存变量 来管理 Token,并手动在请求中附加
-
对用户体验要求特别高,且 Token 需随时刷新
- 可以采用内存变量 + Refresh Token模式,在刷新或重新打开页面时进行"无感刷新"获取新 Token。适当平衡安全与体验。
-
普通 Cookie(可读写)
- 并不建议在生产环境中使用,因为极易被 XSS 窃取。
8. 结论
- 最安全的通用做法:把 JWT 放在 HttpOnly + Secure Cookie 中,配合 CSRF 防护和 HTTPS,全流程加密传输,阻断常见攻击手段。
- 跨域更灵活的做法 :使用 localStorage(或 sessionStorage/内存变量)并在每次请求中手动加
Authorization
头,但要确保站点足够安全,尤其防范 XSS。 - 选择依据 :主要看你对"安全 "和"跨域可行性"的权衡,也要结合自身的技术栈、业务复杂度和用户体验要求来做决定。
希望以上对比能帮助你更好地理解和选择合适的 Token 存储方式。若应用对安全要求很高,优先考虑 HttpOnly Cookie 方案 ;若需要同时支持多域名访问或移动端/第三方,则要结合 Token + localStorage/内存,并实施严格的前端安全策略。