前端登录token到底应该存在哪?LocalStorage、SessionStorage还是Cookie?一篇说透!

点击上方 React中文社区,关注公众号
回复1,加入高级React交流群

作者:ErpanOmer

https://juejin.cn/post/7521936882353471526

如果你做过任何需要登录的功能,那么你一定思考过这个问题:当后端甩给我一个token时,我一个前端,到底应该把它放在哪儿?

这个问题看似简单,无非就是 LocalStorageSessionStorageCookie 三个选项。但如果我告诉你,一个错误的选择,可能会直接导致你的网站出现严重的安全漏洞,你是不是会惊出一身冷汗?

许多开发者(包括曾经的我)不假思索地把token塞进LocalStorage,因为它的API最简单好用。但这种方便的背后,隐藏着巨大的风险。

今天,这篇文章将带你彻底终结这个纠结。我们将深入对比这三位“候选人”的优劣,剖析它们各自面临的安全威胁(XSS 和 CSRF),并最终给出一个当前业界公认的最佳实践方案。

1. 三种存储方案对比

在做决定前,我们先来快速了解一下这三个Web存储方案的基本特性。

特性

`LocalStorage`

`SessionStorage`

`Cookie`

**生命周期**

永久,除非手动清除

页面会话期间(标签页关闭即失效)

可设置过期时间

**存储大小**

约 5MB

约 5MB

约 4KB

**JS可访问性**

可访问

可访问

可访问(除非设置`HttpOnly`)

**与服务端通信**

不会自动发送

不会自动发送

**每次HTTP请求都会自动携带**

一目了然,LocalStorageSessionStorage是HTML5提供的新API,更大、更易用。而Cookie是“老前辈”,小而精,并且有个独一无二的特性:会自动“粘”在HTTP请求头里发给后端。

2. 两大安全攻击 XSS 与 CSRF

选择存储方案,本质上是在权衡安全和便利。而威胁token安全的主要是下面两种。

XSS (跨站脚本攻击)
  • 手法:攻击者通过某种方式(比如评论区)向你的网站注入了恶意的JavaScript脚本。当其他用户访问这个页面时,这段脚本就会执行。

  • 目标:如果你的token存在LocalStorageSessionStorage里,那么这段恶意脚本就可以通过简单的localStorage.getItem('token')轻松地把它偷走,然后发送到攻击者的服务器。token失窃,你的账户就被冒充了。

结论一:LocalStorage 和 SessionStorage 对 XSS 攻击是完全不设防的。只要你的网站存在XSS漏洞,存在里面的任何数据都能被轻易窃取。

CSRF (跨站请求伪造)
  • 手法:你刚刚登录了你的银行网站bank.com,你的登录凭证(Cookie)被浏览器记住了。然后,你没有关闭银行页面,而是点开了一个恶意网站hacker.com。这个恶意网站的页面里可能有一个看不见的表单或<img>标签,它会自动向bank.com/transfer这个地址发起一个转账请求。

  • 目标:因为浏览器在发送请求到bank.com时,会自动带上bank.comCookie,所以银行服务器会认为这个请求是你本人发起的,于是转账就成功了。你神不知鬼不觉地被“伪造”了意愿。

结论二:Cookie 如果不加以保护,会受到 CSRF 攻击的威胁。

3. 现代Cookie的“优势”

看到这里你可能会想:LocalStorage防不住XSS,Cookie防不住CSRF,这可怎么办?

别急,我们的Cookie经过多年的进化,已经有了强大的防止手段。

HttpOnly - 封印JS的访问

如果在设置Cookie时,加上HttpOnly属性,那么通过JavaScript(如 document.cookie)将无法读取到这个Cookie

Set-Cookie: token=...; HttpOnly

这意味着,即使网站存在XSS漏洞,攻击者的恶意脚本也偷不走这个Cookie,从根本上阻断了XSS利用token的路径。

SameSite - 防止携带

SameSite属性用来告诉浏览器,在跨站请求时,是否应该携带这个Cookie。它有三个值:

  • Strict:最严格。只有当请求的发起方和目标网站完全一致时,才会携带Cookie,能完全防御CSRF。

  • Lax:比较宽松(现在是大多数浏览器的默认值)。允许在“顶级导航”(如<a>链接、GET表单)的跨站请求中携带Cookie,但在<img><iframe>、POST表单等“嵌入式”请求中会拦截。这已经能防御大部分CSRF攻击了。

  • None:最松。任何情况下都携带Cookie。但必须同时指定Secure属性(即Cookie只能通过HTTPS发送)。

对于登录token,我们通常希望它尽可能安全,所以SameSite=Strict是最佳选择。

Secure - 保证传输安全

这个属性很简单,只要设置了它,Cookie就只会在HTTPS的加密连接中被发送,可以防止在传输过程中被窃听。

4. 终极答案

综合以上所有分析,我们终于可以给出当前公认的最佳、最安全的方案了。

这个方案的核心是“组合拳”:将不同生命周期的token存放在不同的地方,各司其职。

我们通常有两种token

  • **AccessToken**:生命周期很短(如15分钟),用于访问受保护的API资源。

  • **RefreshToken**:生命周期很长(如7天),专门用来在AccessToken过期后,换取一个新的AccessToken

最佳存储策略如下:

  1. RefreshToken: 存放在一个 HttpOnly=trueSecure=trueSameSite=Strict 的Cookie中。

    *   **为什么?** `RefreshToken`非常关键且长期有效,所以必须用最安全的方式存储。`HttpOnly`让它免受XSS攻击,`SameSite=Strict`让它免受CSRF攻击。前端 JS 完全接触不到它,只在需要刷新`token`时,由浏览器自动带着它去请求`/refresh_token`这个特定接口。
  2. AccessToken:  存放在 JavaScript的内存中(例如,一个全局变量、React Context或Vuex/Pinia等状态管理库里)。

    *   **为什么?** `AccessToken`需要被JS读取,并放在HTTP请求的`Authorization`头里(`Bearer xxx`)发送给后端。将它放在内存中,可以避免XSS直接从`LocalStorage`里扫荡。当用户关闭标签页或刷新页面时,内存中的`AccessToken`会丢失。
  • 丢失了怎么办? 这就是RefreshToken发挥作用的时候了。当应用启动或AccessToken失效时,我们就向后端发起一个请求(比如访问/refresh_token接口),浏览器会自动带上我们安全的RefreshTokenCookie,后端验证通过后,就会返回一个新的AccessToken,我们再把它存入内存。

这个方案完美地结合了安全性和可用性,几乎无懈可击。

一张表格说透

存储方式

优点

缺点(安全风险)

推荐用法

`LocalStorage`

API简单,容量大,持久

**XSS**

**不推荐**存储敏感信息(如Token)

`SessionStorage`

API简单,标签页关闭即删

**XSS**

同上

`Cookie`

可自动发送,可配置安全属性

**CSRF** (若无`SameSite`)

不推荐直接存`AccessToken`

**内存 + `HttpOnly` Cookie**

**安全** (防XSS+CSRF), **体验好**

方案略复杂

**最佳实践** (`AccessToken`存内存,`RefreshToken`存`HttpOnly Cookie`)

希望这篇文章能彻底帮你理清思路。当你在实践中或者面试被问到时,就可以把这套“方案”发挥出来。

最后
送人玫瑰,手留余香,觉得有收获的朋友可以点赞,关注一波 ,我们组建了高级前端交流群,如果您热爱技术,想一起讨论技术,交流进步,不管是面试题,工作中的问题,难点热点都可以在交流群交流,为了拿到大Offer,邀请您进群,入群就送前端精选100本电子书以及 阿里面试前端精选资料 添加 下方小助手二维码或者扫描二维码 就可以进群。让我们一起学习进步.


“在看和转发”就是最大的支持
Token 存放在 sessionStoragelocalStorage 各有特点,以下是二者的比较及选择分析: ### 存储特性 - **生命周期**:sessionStorage 的生命周期是页面关闭时自动清除;而 localStorage 是写入后一直存在,除非手动清除。如果希望在用户关闭浏览器后 Token 失效,可选择 sessionStorage;若需要 Token 长期有效,即使浏览器关闭重新打开也能使用,则应选择 localStorage [^2] [^3] [^5]。 - **存储大小**:二者空间都比较大,大约 5M,在存储大小方面基本没有差异 [^2]。 ### 安全性 - **XSS 风险**:将 Token 存储在 webStorage(localStoragesessionStorage)中,可以通过同域的 js 访问,容易受到 XSS 攻击,特别是项目中引入很多第三方 js 库的情况下,如果 js 脚本被盗用,攻击者就可以轻易获取 Token 访问网站。在这方面,二者面临的风险相同 [^1] [^4]。 ### 请求携带方式 - **与请求的关联**:存储sessionStorage 中时,每次调用接口,需要把它作为一个字段传给后台;存储localStorage 中,每次调用接口时通常放在 http 请求头里面。二者都不会像 cookie 那样在每次请求时自动发送 [^1]。 ### 选择建议 - 如果 Token 与当前会话强相关,仅在当前页面会话期间使用,关闭浏览器后不需要保留,例如一些临时的、一次性的登录验证场景,使用 sessionStorage 更合适。 - 如果 Token 需要长期有效,不受浏览器关闭的影响,方便用户在后续多次访问中持续使用,例如一些需要保持长期登录状态的场景,选择 localStorage 更合适。 ```javascript // 示例:将 Token 存储localStorage localStorage.setItem('token', 'your_token_value'); // 获取 localStorage 中的 Token const tokenFromLocal = localStorage.getItem('token'); // 示例:将 Token 存储sessionStorage sessionStorage.setItem('token', 'your_token_value'); // 获取 sessionStorage 中的 Token const tokenFromSession = sessionStorage.getItem('token'); ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值