JWT(JSON Web Token)鉴权:Token 应该存在 LocalStorage 还是 Cookie 中?

JWT 鉴权:Token 存储在 LocalStorage 还是 Cookie 中?——一场关于安全与便利的深度探讨

大家好,欢迎来到今天的讲座。我是你们的技术导师,今天我们要深入探讨一个看似简单却极其关键的问题:

JWT(JSON Web Token)应该存在 LocalStorage 还是 Cookie 中?

这个问题在前端开发中频繁出现,尤其是在使用单页应用(SPA)、微前端架构或前后端分离项目时。很多开发者凭直觉选择其中一种方式,但往往忽略了背后的安全性、兼容性、易用性和业务场景差异

我们将从以下几个维度展开:

  1. 什么是 JWT?
  2. LocalStorage vs Cookie 的基本区别
  3. 安全风险对比(XSS、CSRF)
  4. 实际代码示例:如何分别存储和读取
  5. 最佳实践建议 + 表格总结
  6. 常见误区澄清

一、什么是 JWT?

JWT 是一种开放标准(RFC 7519),用于在网络应用环境间安全地传输信息。它由三部分组成:

  • Header:声明类型(JWT)和签名算法(如 HMAC SHA256)
  • Payload:包含用户身份、权限等自定义数据(可被解码)
  • Signature:防止篡改,通过密钥对前两部分进行签名

例如一个典型的 JWT 字符串如下:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

它通常作为 HTTP 请求头中的 Authorization: Bearer <token> 发送给后端进行验证。


二、LocalStorage 和 Cookie 的本质区别

特性 LocalStorage Cookie
存储位置 浏览器内存(持久化到磁盘) 浏览器缓存区(随请求自动发送)
生命周期 手动清除或关闭浏览器才失效 可设置过期时间(Expires/Max-Age)
是否随请求自动发送 ❌ 不会 ✅ 自动附加到同域请求中
跨域访问 ❌ 同源策略限制 ✅ 可设置 SameSite 属性控制跨站行为
安全性 易受 XSS 攻击 更容易防范 CSRF(配合 SameSite=Strict/Lax)
大小限制 ~5MB(现代浏览器) ~4KB(每个 cookie)

关键点解析:

  • LocalStorage:适合长期保存状态,比如用户登录态,但它不会自动发送给服务器。
  • Cookie:天生为 HTTP 设计,可以配置为仅限 HTTPS、HttpOnly(防 JS 访问)、SameSite(防 CSRF)。

三、安全风险对比:谁更危险?

1. XSS(跨站脚本攻击)

如果攻击者注入恶意脚本(如 <script src="malicious.js">),那么:

  • LocalStorage 中的 Token:会被 JavaScript 直接读取 → 极易被盗!

    // 如果页面被 XSS 注入:
    alert(localStorage.getItem('authToken')); // 👉 攻击者拿到 token!
  • Cookie 中的 Token:若设置了 HttpOnly=true,JavaScript 无法访问 → 更安全!

✅ 推荐做法:将 JWT 存放在带 HttpOnly 标志的 Cookie 中,避免 XSS 泄露。

2. CSRF(跨站请求伪造)

这是另一个经典问题:攻击者诱导用户点击链接,触发非预期操作。

  • LocalStorage 方案:因为没有自动携带 Token,所以 CSRF 成本高(需要手动加 header)→ 相对安全。
  • Cookie 方案:默认会随请求发送,容易被利用(除非启用 SameSite=Strict/Lax)。

⚠️ 注意:如果你把 JWT 放在 Cookie 中但未设置 SameSite=StrictLax,就等于给 CSRF 开门!

✅ 正确做法:使用 SameSite=LaxStrict + Secure(HTTPS)+ HttpOnly 组合。


四、实际代码示例:两种方案实现

示例场景:用户登录成功后,保存 JWT 并在后续请求中带上它

✅ 方案一:存储在 Cookie 中(推荐用于大多数 Web 应用)

// 登录接口返回后,设置 HttpOnly + Secure + SameSite=Strict 的 Cookie
function setAuthCookie(token) {
    document.cookie = `authToken=${token}; Path=/; Secure; HttpOnly; SameSite=Strict`;
}

// 获取 Cookie 中的 Token(只能后端处理,前端不能直接读)
function getAuthTokenFromCookie() {
    const cookies = document.cookie.split('; ');
    for (let cookie of cookies) {
        const [name, value] = cookie.split('=');
        if (name === 'authToken') return value;
    }
    return null;
}

// 前端发起请求时,必须靠 axios 拦截器自动附带 Cookie(由浏览器自动完成)
axios.interceptors.request.use(config => {
    config.withCredentials = true; // 必须显式开启,否则不发 Cookie
    return config;
});

后端 Node.js Express 示例(Express.js + cookie-parser):

app.use(cookieParser());

app.post('/login', (req, res) => {
    const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET, { expiresIn: '1h' });

    res.cookie('authToken', token, {
        httpOnly: true,
        secure: true,      // 仅 HTTPS 使用
        sameSite: 'strict', // 防止 CSRF
        maxAge: 3600000    // 1小时
    });

    res.json({ success: true });
});

✅ 方案二:存储在 LocalStorage 中(适用于某些特殊场景)

// 登录成功后保存到 localStorage
function saveAuthTokenToLS(token) {
    localStorage.setItem('authToken', token);
}

// 从 localStorage 获取 Token
function getAuthTokenFromLS() {
    return localStorage.getItem('authToken');
}

// 在 axios 请求拦截器中手动添加 Authorization Header
axios.interceptors.request.use(config => {
    const token = getAuthTokenFromLS();
    if (token) {
        config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
});

⚠️ 缺点:前端 JS 可以随意读写,一旦 XSS 成功,Token 立刻暴露!


五、最佳实践建议(结合业务场景)

场景 推荐方式 理由
单页应用(SPA) ✅ Cookie + HttpOnly + SameSite=Strict 安全性强,自动携带,适合 React/Vue/Angular
移动 Web App(WebView) ✅ Cookie(iOS Safari 限制较多) 需注意 iOS 上 Cookie 的兼容性问题
API 服务调用(无前端 UI) ✅ Header(Bearer Token) 不涉及浏览器存储,无需考虑 XSS
微前端架构(多个子应用共用鉴权) ✅ Cookie(统一管理) 各子应用共享同一域名下的 Cookie,便于统一登出
对安全性要求极高(金融类) ✅ Cookie + HttpOnly + SameSite=Strict + Refresh Token 多层防护,降低泄露风险
快速原型开发 / 内部工具 ✅ LocalStorage 快速调试方便,但上线前务必替换

📌 小贴士:

  • 永远不要同时使用两者!
  • 不要把敏感信息放 Payload 中(如密码、身份证号)
  • 定期刷新 Token(Refresh Token + Access Token 分离机制)

六、常见误区澄清

❌ 误区 1:“我用了 LocalStorage 就很安全”

❌ 错误!LocalStorage 没有任何保护机制,只要页面被注入脚本,就能窃取 Token。尤其在 SPA 中,大量第三方库可能引入漏洞。

❌ 误区 2:“我把 Token 放 Cookie 就一定能防 CSRF”

❌ 错误!必须配合 SameSite=StrictLax,否则仍然可能被跨站请求利用。

❌ 误区 3:“HttpOnly 的 Cookie 前端就不能用了”

✅ 正确!但你可以通过后端代理(如 /api/me 返回当前用户信息)来间接获取用户上下文,而不是让前端直接读 Cookie。

❌ 误区 4:“所有项目都应该用 Cookie”

❌ 错误!有些场景不适合 Cookie(如移动端 WebView、嵌套 iframe),此时可用 LocalStorage 或 Header 方式。


结语:选择不是“哪个更好”,而是“哪个更适合”

我们不是在争论 LocalStorage 和 Cookie 的优劣,而是在理解它们各自的适用边界。

  • 如果你追求极致的安全性和自动管理(尤其是企业级 Web 应用),请优先选择 带 HttpOnly、Secure、SameSite=Strict 的 Cookie
  • 如果你在做快速原型、内部系统或移动 H5 页面,且能接受一定风险,Local Storage 也可以作为一种妥协方案。

记住一句话:

安全不是完美的,而是权衡后的最优解。

希望今天的分享让你对 JWT 存储有了更清晰的认知。下次再遇到这个问题时,不妨问问自己:“我的业务场景是否允许 XSS?是否担心 CSRF?是否需要跨域支持?” —— 答案就在那里。

谢谢大家!欢迎留言讨论你的实战经验 😊

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注