实现方式:
核心原理 Session Storage 隔离:
核心机制:Session Storage + Cookie 前缀隔离
使用 sessionStorage 存储当前会话用户标识和 token,每个标签页独立
Cookie 前缀隔离:使用 user_{username}_ 前缀为每个用户单独存储记住密码信息
Token 隔离存储:将每个用户的 token 以 token_{username} 为键存储在 sessionStorage 中工作流程
1.用户在标签页A登录用户A,在标签页B登录用户B
2.每个标签页的 sessionStorage 中存储各自的 currentSessionUser 标识
3.每个用户的 token 以用户名为键分别存储
4.Cookie 中每个用户的记住密码信息也以用户名为前缀存储
5.这样实现了同一浏览器中多个标签页可以登录不同用户且互不干扰
这个实现方案确保了在同一浏览器中可以同时登录多个不同账户,每个标签页维护自己的会话状态,不会相互影响。
1.修改登录逻辑
//login.vue
handleLogin() {
this.$refs.loginForm.validate((valid) => {
if (valid) {
this.loading = true;
// 使用用户名作为键前缀存储记住密码信息
const userPrefix = `user_${this.loginForm.username}_`;
if (this.loginForm.rememberMe) {
Cookies.set(userPrefix + "username", this.loginForm.username, {
expires: 30,
});
Cookies.set(
userPrefix + "password",
encrypt(this.loginForm.password),
{ expires: 30 }
);
Cookies.set(userPrefix + "rememberMe", this.loginForm.rememberMe, {
expires: 30,
});
} else {
Cookies.remove(userPrefix + "username");
Cookies.remove(userPrefix + "password");
Cookies.remove(userPrefix + "rememberMe");
}
// 正常调用登录,但保存会话信息
this.$store
.dispatch("Login", this.loginForm)
.then((res) => {
console.log("登录成功", res);
// 登录成功后设置当前会话标识
sessionStorage.setItem(
"currentSessionUser",
this.loginForm.username
);
// 确保token已经保存到sessionStorage
if (res && res.token) {
sessionStorage.setItem(
`token_${this.loginForm.username}`,
res.token
);
}
// 使用setTimeout确保DOM更新完成后再跳转
setTimeout(() => {
this.$router
.push({ path: this.redirect || "/" })
.then(() => {
console.log("路由跳转成功");
})
.catch((err) => {
console.error("路由跳转失败:", err);
});
}, 100);
})
.catch((error) => {
console.error("登录失败:", error);
this.loading = false;
if (this.captchaOnOff) {
this.getCode();
}
});
}
});
},
2.获取cookie逻辑
getCookie() {
// 获取当前会话用户(如果存在)
const currentSessionUser = sessionStorage.getItem("currentSessionUser");
if (currentSessionUser) {
// 如果有当前会话用户,优先使用该用户的记住密码信息
const userPrefix = `user_${currentSessionUser}_`;
const username = Cookies.get(userPrefix + "username");
const password = Cookies.get(userPrefix + "password");
const rememberMe = Cookies.get(userPrefix + "rememberMe");
this.loginForm = {
username: username === undefined ? this.loginForm.username : username,
password:
password === undefined
? this.loginForm.password
: decrypt(password),
rememberMe: rememberMe === undefined ? false : Boolean(rememberMe),
};
} else {
// 查找任意用户的记住密码信息
const allCookies = Cookies.get();
for (const key in allCookies) {
if (key.startsWith("user_") && key.endsWith("_username")) {
const userPrefix = key.replace("username", "");
const username = Cookies.get(userPrefix + "username");
const password = Cookies.get(userPrefix + "password");
const rememberMe = Cookies.get(userPrefix + "rememberMe");
this.loginForm = {
username: username || this.loginForm.username,
password: password ? decrypt(password) : this.loginForm.password,
rememberMe: rememberMe ? Boolean(rememberMe) : false,
};
break;
}
}
}
},
3.修改 Vuex Store 中的 Login Action
// store/modules/user.js 或类似文件
const user = {
state: {
token: getToken(), // 使用我们修改后的getToken函数
name: "",
avatar: "",
id: "",
nick: "",
roles: [],
permissions: [],
unreadCount: 0, //消息未读数据
},
// 登录
Login({ commit }, userInfo) {
const { username, password, code, uuid } = userInfo;
return new Promise((resolve, reject) => {
login(username, password, code, uuid).then(res => {
// 设置token到store和sessionStorage
commit('SET_TOKEN', res.token);
// 同时保存到sessionStorage实现会话隔离
sessionStorage.setItem(`token_${username}`, res.token);
resolve(res);
}).catch(error => {
reject(error);
});
});
},
// 退出系统
LogOut({ commit, state }) {
return new Promise((resolve, reject) => {
logout(state.token)
.then(() => {
commit("SET_TOKEN", "");
commit("SET_ROLES", []);
commit("SET_PERMISSIONS", []);
removeToken();
// 清除当前会话标识
sessionStorage.removeItem('currentSessionUser');
resolve();
})
.catch((error) => {
reject(error);
});
});
},
// 前端 登出
FedLogOut({ commit }) {
return new Promise((resolve) => {
commit("SET_TOKEN", "");
removeToken();
// 清除当前会话标识
sessionStorage.removeItem('currentSessionUser');
resolve();
});
}
4.修改 utils/auth.js 中的 Token 管理函数
// 如果该文件不存在,请创建此文件
// utils/auth.js 或类似文件
const TokenKey = 'Admin-Token';
// 获取当前有效的token
export function getToken() {
// 首先尝试从sessionStorage获取当前用户的token
const currentUser = sessionStorage.getItem('currentSessionUser');
if (currentUser) {
const userToken = sessionStorage.getItem(`token_${currentUser}`);
if (userToken) {
return userToken;
}
}
// 回退到localStorage或cookies
return localStorage.getItem(TokenKey) || '';
}
// 设置token
export function setToken(token) {
const currentUser = sessionStorage.getItem('currentSessionUser');
if (currentUser) {
// 保存到sessionStorage实现标签页隔离
sessionStorage.setItem(`token_${currentUser}`, token);
} else {
// 默认保存方式
localStorage.setItem(TokenKey, token);
}
}
// 移除token
export function removeToken() {
const currentUser = sessionStorage.getItem('currentSessionUser');
if (currentUser) {
sessionStorage.removeItem(`token_${currentUser}`);
} else {
localStorage.removeItem(TokenKey);
}
sessionStorage.removeItem('currentSessionUser');
}```
5.在应用初始化时恢复会话,
//在 src/main.js 或 src/App.vue 的 created 钩子中添加:
import store from '@/store';
created() {
// 应用初始化时确保token正确设置
const currentUser = sessionStorage.getItem("currentSessionUser");
if (currentUser) {
const token = sessionStorage.getItem(`token_${currentUser}`);
if (token) {
store.commit("SET_TOKEN", token);
}
}
},
1976

被折叠的 条评论
为什么被折叠?



