superleeyom / blog

:bookmark: 个人博客仓库,用于记录一些幼稚的想法和脑残的瞬间,欢迎 star、watch,该仓库为个人博客,请不要提 issue ,该仓库后端参考了 @yihong0618 的 gitblog 项目,前端参考了@LoeiFy 的 Mirror 项目,感谢!

Home Page:https://blog.leeyom.top

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

同一浏览器不同用户登录冲突问题探究

superleeyom opened this issue · comments

问题

由于业务扩展问题,目前公司有 a 和 b 两个账号中心服务,分别对应的是运营端和服务商端,这两个账号系统的访问域名分别是a.aqara.cnb.aqara.cn,其中 b 账号中心由其他的团队负责开发,用户登录成功后,会返回用户的信息(userInfo)和访问令牌(token),前端会将他们缓存在客户端的 Cookies里面,由于共用同一个二级域名(.aqara.cn),前端Cookies 里面缓存的数据是共用的。

就会存在这种问题:同一个浏览器,用户在标签 A 登录A用户,然后又重新打开标签页 B,登录用户 B,这样就会导致,第二个用户会把第一个用户的信息覆盖掉,但是此时用户无感知,Cookies 里面存储的令牌和用户信息就会被覆盖掉。这样的话假如请求的数据(比如查看个人信息)是基于 token 拿用户信息的话,由于后台的网关层,有把 token 作为键,用户信息作为 value,缓存用户用户信息,有时候就会导致 A 用户拿到 B 用户的数据。如果恰好 a 用户和 b 用户都有访问某接口的权限,就会造成,怎么我操作后,显示的操作人确实另外一个人的名字。

解决方案

  • 简单粗暴

    • 修改 a 和 b 两个账号中心服务的访问域名,访问域名分别是a.aqara.cnb.aqara.com,由于是不同的二级域名,这样前端的 Cookies就是隔离开来的,相关之间不会有任何的影响。 这种得确认更换域名后,会不会影响其他的业务。
  • 服务端

    • 在用户登录的时候,返回用户的数据,如果不想被覆盖,只能换成不一样的,可以设置为用户名 (username)+sessionkey使每一个用户的 sessionkey 都不一样,但是由于 b 账号中心的登录接口不是我们掌控的,此方案不太好实施。

    • 用户登陆后分配一个临时标识(sid),所有的请求和响应均携带此标识,后台用来区分用户,实际就是将判断上移到应用层面。一边这个标识生成后会放到 redis,设置一定的有效时间。伪代码如下:

      String sid = request.getReuqestParam("sid");
      String token = request.getReuqestParam("token");
      String userId = redis.get("sid");
      if(StrUtil.isBlank(userId)){
      	throw new BizException("当前用户不存在");
      }
      String userIdFromToen = JwtUtil.parseToken("token");
      if(!userIdFromToen.equal(userId)){
      	throw new BizException("当前用户已被替换");
      }
    • 用户在登出后,把服务端要及时的缓存的用户信息给清除掉。

  • 前端:

    • 登陆成功后将 sid 后存储到本地,在每个需要验证的页面加上这个参数,当用户刷新页面时与本地存储的值进行比较,不符合就跳转登陆页(或弹出提示框,提醒当前用户已更换用户,是否继续执行此操作,用户刷新页面后,就会刷新当前用户的菜单权限,用户信息等等)。

    • 后登陆的用户会覆盖上一个用户的本地值,而 url 里的参数不会变所以会导致 URL 中获取的值和本地不一致,目前 qq 邮箱采用也是这种方式。

    • 前端伪代码:

      router.beforeEach((to, from, next) => {
          // 工具方法,获取url地址中sid的值
          let urlSid = getPop('sid');
          // 获取localStorege中sid的值
          let localSid = getLocalStorage('sid').sid
          console.log('url=%s,local=%s ', urlSid, localSid)
          // 先判断两个参数是否存在
          if (urlSid && localSid) {
            if (urlSid == localSid) {
              next()
            } else {
              let url = window.location.href.replace(/[\?,\#]\S*/g, '');
              window.location.href = url;
              next({
                path: "/"
              });
            }
          } 
      })

参考