前端学习路线一条龙【内含入门到进阶到高级精选资源】无套路获取!!!
前端小菜鸡之菜鸡互啄,公众号:前端开发爱好者xy哥怒肝,前端学习路线一条龙【内含入门到进阶到高级精选资源】无套路获取!!!哈喽,大家好 我是
xy
????????????。今天给大家来分享一个bug
????。在同一浏览器
打开两个同源页面
发生的数据串改的问题,当然这篇文章只是用码云来举个例子,其它很多大型网站都中招了 ????
背景
一天下午,某某上帝
(客户就是上帝)正在使用的我们公司的产品,在浏览器打开A
标签页登录了a
账号,然后又到B
标签页登录了b
账号,也就是同时登录了两个账号,然后到A
下去操作数据(不刷新页面),结果造成了在A
下修改到了B
的数据,而且两个账号的权限还存在差异 ????
为什么会被串改
至于为什么会修改到 B
的数据:
前端把用户信息
和 token
之类的是存储在浏览器本地
的,这样一来,打开新的标签页登录其它账户,由于是同源页面
,新的标签页的用户信息和token
肯定会覆盖
掉前一个标签页的用户信息和token
,在页面不刷新的情况下,操作a
账户下的数据,其实是操作的b
下的数据
很多同学遇到这个问题的第一反应:
这个不是很正常的操作
吗或者让用户刷新页面
不就好了,包括我也是这样想的 ????
同时也查看了市面上的一些产品,同样有类似的问题,就拿我们比较熟悉的 码云
来说吧,我特地的注册了两个账号
- 注册的新账号在第一个标签页登录因为是全新的账号,没有任何信息之类的
- 打开新的标签页,先退出登录,然后登录我之前一直使用的账号
账户明明是新的账户,但是却把自己使用的旧账户的数据
给展示过来了
当时心里暗喜,码云
这么大的平台都没做处理,我们应该也无所谓了 ✌️
但是我就是个打工仔
,搞不搞还不是领导说了算吗?既然领导已经提了这个需求,并且强烈要求优化用户体验
,只能想办法解决喽 ????
需求方案
方案 1:
- 同一浏览器支持
同时登录两个账号
,互不影响
方案 2:
- 同一浏览器打开两个同源页面,把
前一个标签页面
提示用户强制刷新
或者直接重定向
到公司官网
回归问题本质
这个问题的本质其实就是:由于是
同源页面
,新的标签页的用户信息和token
会覆盖
掉前一个标签页的用户信息和token
既然本质问题是本地存储替换的问题,那就想办法着手解决喽 ????♂️
如何做到同时登录两个账号
做到两个账号同时登录互不干涉,其实只需要保证本地存储互不影响,让不同的用户存储的 token 的键名不一样
实现方法:在登录的时候存储用userName+token
作为键来存储token
import Cookies from 'js-cookie' export function setToken( token, userName ) { return Cookies.set( userName + 'token', token, { expires: xxxx } ) }
但是需要考虑到这个时候用户名:userName
也可能会被覆盖,所以在每次刷新页面之前,把用户名存储到sessionStorage
中去
window.addEventListener("beforeunload", () => { sessionStorage.setItem("userName", this.userName || ""); });
为什么要放到sessionStorage
中呢 ?
???? 因为
sessionStorage
生命周期为当前窗口
或标签页
,也就是每个标签页中的sessionStorage
互不影响,即使是同源页面
获取 token
直接根据userName+token
获取即可
export function getToken() { return Cookies.get( sessionStorage.getItem( 'userName' ) + 'token' ) }
好了,到这里基本上完美解决想要同一个浏览器登录多个用户账号的问题
前一个标签强制刷新或者重定向
再次回到问题本身,还是本地存储的问题。打开第二个同源页面标签页,有没有办法能够告诉第一个标签页呢???
当然有办法了,我们可以利用 HTML5 storage事件监听
:
????
HTML5 storage事件监听
:当同源页面的某个页面修改
了localStorage
,其余的同源页面只要注册了storage
事件,就会触发
Web Storage API
内建了一套事件通知机制,当存储区域的内容发生改变(包括增加
、修改
、删除
数据)时,就会自动触发 storage
事件,并把它发送给所有感兴趣的监听者
。因此,如果需要跟踪存储区域的改变,就需要在关心存储区域内容的页面监听storage
事件。
window.addEventListener("storage", (e)=>{ // 获取 e 后做一系列判断操作 }, false);
实际上,这个事件e
上还带有很多信息,可以对事件做精确的控制 ????。
字段 | 含义 |
key | 发生变化的 storageKey |
newValue | 变换后新值 |
oldValue | 变换前原值 |
storageArea | 相关的变化对象 |
url | 触发变化的 URL,如果是 frameset 内,则是触发帧的 URL |
有了这些内容,就可以根据自己的业务需求来做需要的操作了。
但是这里注意一个问题,只有当同源页面的某个页面修改
了localStorage
,也就是不修改
,还是监听不到
的。
如果直接把第一个标签页的链接复制到第二个标签页,本地存储是不会更改的
,这个时候第一个标签页的监听事件将不会触发
这个时候可以在每次页面初始化的时候在 localStorage
中写入一个唯一的标识
,建议是时间戳
之类的,这样即使把地址复制到第二个标签页中,也会执行初始化操作,时间戳改变
就会触发监听的storage
事件。
ok 了,问题统统解决
???? 最后想
@码云
以及很多存在此类问题的网站都提出这个优化
点,2022
,让我们变得更好吧 ????。