现象
客户在浏览器的两个页签里先后使用不同的账号(先 A 后 B)登录了我们的系统,发现账号 B 加完车未结算,切换到账号 A 所在的页签里点击结算,结果账号 B 的钱被扣了。
分析原因
由于我们系统里的 cookie 是种在了同一个域名下,所以后登录的账号 B 会替换掉先登录的账号 A,这样导致用户发送请求时携带的 cookie 都变成了 B。
解决方案
假设大家的项目里已有一个通用的请求方法,这在架构设计初期都会封装好的,通常方法名叫 request
,核心代码就是要写在这里面。
以下流程是针对所有账号的,这里以账号 A 为例
- 首先判断是否有账号 A 的缓存,如果没有就写入,key 为唯一标志(可区分开不同用户,这里以账号为例),值是触发接口时的时间戳(可以是
Date.now()
) - 如果有账号 A 的缓存,就收集
localStorage
里的所有账号(这里是 A 和 B),可以放到一个数组中缓存起来 - 遍历数组,找到最大时间戳对应的缓存的 key,这里假设是 B
- 如果这个最大时间戳对应的缓存的 key 和当前账号(假设是 B)匹配,那么就正常操作
- 如果当前操作的账号(假设是 A)不是最大时间戳对应的 key,就不发送任何接口请求,同时清除掉账号 A 里的所有缓存(这里是 A 和 B)并刷新页面
- 此时所有页签对应的页面都是账号 B 的了
模拟代码
const request = () => {
if(!localStorage.getItem('账号 A')) {
// 如果缓存不存在,添加缓存
localStorage.setItem('账号 A', Date.now())
} else {
// 如果缓存存在,遍历 localStorage,并缓存到一个数组中
for(let account in localStorage) {
arr.push({
[account]: localStorage.getItem(account)
});
}
// 封装一个方法,遍历数组中的每一项,找到最大时间戳对应的账号
let max = getMax(arr); // getMax 方法体省略...
if(max === '账号 A') {
// 如果最大时间戳对应的账号是当前页签对应的账号,就正常操作,包括更新缓存
localStorage.setItem('账号 A', String(Date.now()))
} else {
// 否则,刷新页面,清空所有账号,并且不发送任何请求
location.reload();
for(let account in localStorage) {
localStorage.removeItem(account)
}
return;
}
}
}
写在最后
此方案可能不是最佳方案,希望可以抛砖引玉!