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

优采云 发布时间: 2022-05-07 22:22

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

  哈喽,大家好 我是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'<br /><br />export function setToken( token, userName ) {<br />  return Cookies.set( userName + 'token', token, { expires: xxxx } )<br />}<br />

  但是需要考虑到这个时候用户名:userName也可能会被覆盖,所以在每次刷新页面之前,把用户名存储到sessionStorage中去

  window.addEventListener("beforeunload", () => {<br />  sessionStorage.setItem("userName", this.userName || "");<br />});<br />

  为什么要放到sessionStorage中呢 ?

  因为sessionStorage生命周期为当前窗口或标签页,也就是每个标签页中的sessionStorage互不影响,即使是同源页面

  获取 token 直接根据userName+token获取即可

  export function getToken() {<br />  return Cookies.get( sessionStorage.getItem( 'userName' ) + 'token' )<br />}<br />

  好了,到这里基本上完美解决想要同一个浏览器登录多个用户账号的问题

  前一个标签强制刷新或者重定向

  再次回到问题本身,还是本地存储的问题。打开第二个同源页面标签页,有没有办法能够告诉第一个标签页呢???

  当然有办法了,我们可以利用 HTML5 storage事件*敏*感*词*:

  HTML5 storage事件*敏*感*词*:当同源页面的某个页面修改了localStorage,其余的同源页面只要注册了storage事件,就会触发

  Web Storage API内建了一套事件通知机制,当存储区域的内容发生改变(包括增加、修改、删除数据)时,就会自动触发 storage 事件,并把它发送给所有感兴趣的*敏*感*词*者。因此,如果需要跟踪存储区域的改变,就需要在关心存储区域内容的页面*敏*感*词*storage事件。

   window.addEventListener("storage", (e)=>{<br />   // 获取 e 后做一系列判断操作<br /> }, false);<br />

  实际上,这个事件e上还带有很多信息,可以对事件做精确的控制 。

  字段含义

  key

  发生变化的 storageKey

  newValue

  变换后新值

  oldValue

  变换前原值

  storageArea

  相关的变化对象

  url

  触发变化的 URL,如果是 frameset 内,则是触发帧的 URL

  有了这些内容,就可以根据自己的业务需求来做需要的操作了。

  但是这里注意一个问题,只有当同源页面的某个页面修改了localStorage,也就是不修改,还是*敏*感*词*不到的。

  如果直接把第一个标签页的链接复制到第二个标签页,本地存储是不会更改的,这个时候第一个标签页的*敏*感*词*事件将不会触发

  这个时候可以在每次页面初始化的时候在 localStorage 中写入一个唯一的标识,建议是时间戳之类的,这样即使把地址复制到第二个标签页中,也会执行初始化操作,时间戳改变就会触发*敏*感*词*的storage事件。

  ok 了,问题统统解决

  最后想@码云以及很多存在此类问题的网站都提出这个优化点,2022,让我们变得更好吧 。

  写在最后

  公众号:前端开发爱好者 专注分享 web 前端相关技术文章、视频教程资源、热点资讯等,如果喜欢我的分享,给 点一个赞 或者 ➕关注 都是对我最大的支持。

  欢迎长按图片加好友,我会第一时间和你分享前端行业趋势,面试资源,学习途径等等。

  

  添加好友备注【进阶学习】拉你进技术交流群

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线