同一浏览器不同用户登录冲突问题探究
superleeyom opened this issue · comments
问题
由于业务扩展问题,目前公司有 a 和 b 两个账号中心服务,分别对应的是运营端和服务商端,这两个账号系统的访问域名分别是a.aqara.cn
和b.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.cn
和b.aqara.com
,由于是不同的二级域名,这样前端的Cookies
就是隔离开来的,相关之间不会有任何的影响。 这种得确认更换域名后,会不会影响其他的业务。
- 修改 a 和 b 两个账号中心服务的访问域名,访问域名分别是
-
服务端
-
在用户登录的时候,返回用户的数据,如果不想被覆盖,只能换成不一样的,可以设置为用户名
(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: "/" }); } } })
-