且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

同一浏览器打开两个同源页面引发的bug【@码云 提个小优化】

更新时间:2022-08-12 21:41:36

前端学习路线一条龙【内含入门到进阶到高级精选资源】无套路获取!!!

前端小菜鸡之菜鸡互啄,公众号:前端开发爱好者xy哥怒肝,前端学习路线一条龙【内含入门到进阶到高级精选资源】无套路获取!!!

哈喽,大家好 我是xy????????‍????。今天给大家来分享一个bug????。在同一浏览器打开两个同源页面发生的数据串改的问题,当然这篇文章只是用码云来举个例子,其它很多大型网站都中招了 ????

背景

一天下午,某某上帝(客户就是上帝)正在使用的我们公司的产品,在浏览器打开A标签页登录了a账号,然后又到B标签页登录了b账号,也就是同时登录了两个账号,然后到A下去操作数据(不刷新页面),结果造成了在A下修改到了B的数据,而且两个账号的权限还存在差异 ????

为什么会被串改

至于为什么会修改到 B 的数据:

前端把用户信息token 之类的是存储在浏览器本地的,这样一来,打开新的标签页登录其它账户,由于是同源页面,新的标签页的用户信息和token肯定会覆盖掉前一个标签页的用户信息和token,在页面不刷新的情况下,操作a账户下的数据,其实是操作的b下的数据

很多同学遇到这个问题的第一反应:

这个不是很正常的操作吗或者让用户刷新页面不就好了,包括我也是这样想的 ????

同时也查看了市面上的一些产品,同样有类似的问题,就拿我们比较熟悉的 码云 来说吧,我特地的注册了两个账号

  1. 注册的新账号在第一个标签页登录同一浏览器打开两个同源页面引发的bug【@码云 提个小优化】因为是全新的账号,没有任何信息之类的
  1. 打开新的标签页,先退出登录,然后登录我之前一直使用的账号

同一浏览器打开两个同源页面引发的bug【@码云 提个小优化】同一浏览器打开两个同源页面引发的bug【@码云 提个小优化】账户明明是新的账户,但是却把自己使用的旧账户的数据给展示过来了

当时心里暗喜,码云这么大的平台都没做处理,我们应该也无所谓了 ✌️

但是我就是个打工仔,搞不搞还不是领导说了算吗?既然领导已经提了这个需求,并且强烈要求优化用户体验,只能想办法解决喽 ????

需求方案

方案 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,让我们变得更好吧 ????。