refactor: ♻️ 用户信息不再本地持久化,改为内存态:刷新页面后需要重新请求用户信息
This commit is contained in:
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vue3-element-admin",
|
"name": "vue3-element-admin",
|
||||||
"description": "Vue3 + Vite + TypeScript + Element-Plus 的后台管理模板,vue-element-admin 的 Vue3 版本",
|
"description": "Vue3 + Vite + TypeScript + Element-Plus 的后台管理模板,vue-element-admin 的 Vue3 版本",
|
||||||
"version": "3.2.0",
|
"version": "3.2.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ref, onMounted, onUnmounted, watch, getCurrentInstance } from "vue";
|
import { ref, onMounted, onUnmounted, watch, getCurrentInstance } from "vue";
|
||||||
import { useStomp } from "./useStomp";
|
import { useStomp } from "./useStomp";
|
||||||
import { registerWebSocketInstance } from "@/plugins/websocket";
|
import { registerWebSocketInstance } from "@/plugins/websocket";
|
||||||
import { Auth } from "@/utils/auth";
|
import { AuthStorage } from "@/utils/auth";
|
||||||
|
|
||||||
// 全局单例实例
|
// 全局单例实例
|
||||||
let globalInstance: ReturnType<typeof createOnlineCountHook> | null = null;
|
let globalInstance: ReturnType<typeof createOnlineCountHook> | null = null;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Client, type IMessage, type StompSubscription } from "@stomp/stompjs";
|
import { Client, type IMessage, type StompSubscription } from "@stomp/stompjs";
|
||||||
import { Auth } from "@/utils/auth";
|
import { AuthStorage } from "@/utils/auth";
|
||||||
|
|
||||||
export interface UseStompOptions {
|
export interface UseStompOptions {
|
||||||
/** WebSocket 地址,不传时使用 VITE_APP_WS_ENDPOINT 环境变量 */
|
/** WebSocket 地址,不传时使用 VITE_APP_WS_ENDPOINT 环境变量 */
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useDictSync } from "@/composables/useDictSync";
|
import { useDictSync } from "@/composables/useDictSync";
|
||||||
import { Auth } from "@/utils/auth";
|
import { AuthStorage } from "@/utils/auth";
|
||||||
// 不直接导入 store 或 userStore
|
// 不直接导入 store 或 userStore
|
||||||
|
|
||||||
// 全局 WebSocket 实例管理
|
// 全局 WebSocket 实例管理
|
||||||
|
|||||||
@@ -3,16 +3,17 @@ import { store } from "@/store";
|
|||||||
import AuthAPI, { type LoginFormData } from "@/api/auth.api";
|
import AuthAPI, { type LoginFormData } from "@/api/auth.api";
|
||||||
import UserAPI, { type UserInfo } from "@/api/system/user.api";
|
import UserAPI, { type UserInfo } from "@/api/system/user.api";
|
||||||
|
|
||||||
import { Auth } from "@/utils/auth";
|
import { AuthStorage } from "@/utils/auth";
|
||||||
import { usePermissionStoreHook } from "@/store/modules/permission.store";
|
import { usePermissionStoreHook } from "@/store/modules/permission.store";
|
||||||
import { useDictStoreHook } from "@/store/modules/dict.store";
|
import { useDictStoreHook } from "@/store/modules/dict.store";
|
||||||
import { useTagsViewStore } from "@/store";
|
import { useTagsViewStore } from "@/store";
|
||||||
import { cleanupWebSocket } from "@/plugins/websocket";
|
import { cleanupWebSocket } from "@/plugins/websocket";
|
||||||
|
|
||||||
export const useUserStore = defineStore("user", () => {
|
export const useUserStore = defineStore("user", () => {
|
||||||
const userInfo = useStorage<UserInfo>("userInfo", {} as UserInfo);
|
// 用户信息
|
||||||
|
const userInfo = ref<UserInfo>({} as UserInfo);
|
||||||
// 记住我状态
|
// 记住我状态
|
||||||
const rememberMe = ref(Auth.getRememberMe());
|
const rememberMe = ref(AuthStorage.getRememberMe());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 登录
|
* 登录
|
||||||
@@ -27,7 +28,7 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
const { accessToken, refreshToken } = data;
|
const { accessToken, refreshToken } = data;
|
||||||
// 保存记住我状态和token
|
// 保存记住我状态和token
|
||||||
rememberMe.value = LoginFormData.rememberMe;
|
rememberMe.value = LoginFormData.rememberMe;
|
||||||
Auth.setTokens(accessToken, refreshToken, rememberMe.value);
|
AuthStorage.setTokens(accessToken, refreshToken, rememberMe.value);
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -104,7 +105,7 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
*/
|
*/
|
||||||
function resetUserState() {
|
function resetUserState() {
|
||||||
// 清除用户凭证
|
// 清除用户凭证
|
||||||
Auth.clearAuth();
|
AuthStorage.clearAuth();
|
||||||
// 重置用户信息
|
// 重置用户信息
|
||||||
userInfo.value = {} as UserInfo;
|
userInfo.value = {} as UserInfo;
|
||||||
}
|
}
|
||||||
@@ -113,7 +114,7 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
* 刷新 token
|
* 刷新 token
|
||||||
*/
|
*/
|
||||||
function refreshToken() {
|
function refreshToken() {
|
||||||
const refreshToken = Auth.getRefreshToken();
|
const refreshToken = AuthStorage.getRefreshToken();
|
||||||
|
|
||||||
if (!refreshToken) {
|
if (!refreshToken) {
|
||||||
return Promise.reject(new Error("没有有效的刷新令牌"));
|
return Promise.reject(new Error("没有有效的刷新令牌"));
|
||||||
@@ -124,7 +125,7 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
.then((data) => {
|
.then((data) => {
|
||||||
const { accessToken, refreshToken: newRefreshToken } = data;
|
const { accessToken, refreshToken: newRefreshToken } = data;
|
||||||
// 更新令牌,保持当前记住我状态
|
// 更新令牌,保持当前记住我状态
|
||||||
Auth.setTokens(accessToken, newRefreshToken, Auth.getRememberMe());
|
AuthStorage.setTokens(accessToken, newRefreshToken, AuthStorage.getRememberMe());
|
||||||
resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
@@ -137,7 +138,7 @@ export const useUserStore = defineStore("user", () => {
|
|||||||
return {
|
return {
|
||||||
userInfo,
|
userInfo,
|
||||||
rememberMe,
|
rememberMe,
|
||||||
isLoggedIn: () => !!Auth.getAccessToken(),
|
isLoggedIn: () => !!AuthStorage.getAccessToken(),
|
||||||
getUserInfo,
|
getUserInfo,
|
||||||
login,
|
login,
|
||||||
logout,
|
logout,
|
||||||
|
|||||||
@@ -1,78 +1,43 @@
|
|||||||
import { Storage } from "./storage";
|
import { Storage } from "./storage";
|
||||||
import { AUTH_KEYS } from "@/constants";
|
import { AUTH_KEYS } from "@/constants";
|
||||||
|
|
||||||
/**
|
// 更语义化的命名:仅负责本地凭证与偏好的读写
|
||||||
* 身份验证工具类
|
export const AuthStorage = {
|
||||||
* 集中管理所有与认证相关的功能,包括:
|
getAccessToken(): string {
|
||||||
* - 登录状态判断
|
|
||||||
* - Token 的存取
|
|
||||||
* - 记住我功能的状态管理
|
|
||||||
*/
|
|
||||||
export class Auth {
|
|
||||||
/**
|
|
||||||
* 获取当前有效的访问令牌
|
|
||||||
* 会根据"记住我"状态从适当的存储位置获取
|
|
||||||
* @returns 当前有效的访问令牌
|
|
||||||
*/
|
|
||||||
static getAccessToken(): string {
|
|
||||||
const isRememberMe = Storage.get<boolean>(AUTH_KEYS.REMEMBER_ME, false);
|
const isRememberMe = Storage.get<boolean>(AUTH_KEYS.REMEMBER_ME, false);
|
||||||
// 根据"记住我"状态决定从哪个存储位置获取token
|
|
||||||
return isRememberMe
|
return isRememberMe
|
||||||
? Storage.get(AUTH_KEYS.ACCESS_TOKEN, "")
|
? Storage.get(AUTH_KEYS.ACCESS_TOKEN, "")
|
||||||
: Storage.sessionGet(AUTH_KEYS.ACCESS_TOKEN, "");
|
: Storage.sessionGet(AUTH_KEYS.ACCESS_TOKEN, "");
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
getRefreshToken(): string {
|
||||||
* 获取刷新令牌
|
|
||||||
* @returns 当前有效的刷新令牌
|
|
||||||
*/
|
|
||||||
static getRefreshToken(): string {
|
|
||||||
const isRememberMe = Storage.get<boolean>(AUTH_KEYS.REMEMBER_ME, false);
|
const isRememberMe = Storage.get<boolean>(AUTH_KEYS.REMEMBER_ME, false);
|
||||||
return isRememberMe
|
return isRememberMe
|
||||||
? Storage.get(AUTH_KEYS.REFRESH_TOKEN, "")
|
? Storage.get(AUTH_KEYS.REFRESH_TOKEN, "")
|
||||||
: Storage.sessionGet(AUTH_KEYS.REFRESH_TOKEN, "");
|
: Storage.sessionGet(AUTH_KEYS.REFRESH_TOKEN, "");
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
setTokens(accessToken: string, refreshToken: string, rememberMe: boolean): void {
|
||||||
* 设置访问令牌和刷新令牌
|
|
||||||
* @param accessToken 访问令牌
|
|
||||||
* @param refreshToken 刷新令牌
|
|
||||||
* @param rememberMe 是否记住我
|
|
||||||
*/
|
|
||||||
static setTokens(accessToken: string, refreshToken: string, rememberMe: boolean): void {
|
|
||||||
// 保存"记住我"状态
|
|
||||||
Storage.set(AUTH_KEYS.REMEMBER_ME, rememberMe);
|
Storage.set(AUTH_KEYS.REMEMBER_ME, rememberMe);
|
||||||
|
|
||||||
if (rememberMe) {
|
if (rememberMe) {
|
||||||
// 使用localStorage长期保存
|
|
||||||
Storage.set(AUTH_KEYS.ACCESS_TOKEN, accessToken);
|
Storage.set(AUTH_KEYS.ACCESS_TOKEN, accessToken);
|
||||||
Storage.set(AUTH_KEYS.REFRESH_TOKEN, refreshToken);
|
Storage.set(AUTH_KEYS.REFRESH_TOKEN, refreshToken);
|
||||||
} else {
|
} else {
|
||||||
// 使用sessionStorage临时保存
|
|
||||||
Storage.sessionSet(AUTH_KEYS.ACCESS_TOKEN, accessToken);
|
Storage.sessionSet(AUTH_KEYS.ACCESS_TOKEN, accessToken);
|
||||||
Storage.sessionSet(AUTH_KEYS.REFRESH_TOKEN, refreshToken);
|
Storage.sessionSet(AUTH_KEYS.REFRESH_TOKEN, refreshToken);
|
||||||
// 清除localStorage中可能存在的token
|
|
||||||
Storage.remove(AUTH_KEYS.ACCESS_TOKEN);
|
Storage.remove(AUTH_KEYS.ACCESS_TOKEN);
|
||||||
Storage.remove(AUTH_KEYS.REFRESH_TOKEN);
|
Storage.remove(AUTH_KEYS.REFRESH_TOKEN);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
|
||||||
/**
|
clearAuth(): void {
|
||||||
* 清除所有身份验证相关的数据
|
|
||||||
*/
|
|
||||||
static clearAuth(): void {
|
|
||||||
Storage.remove(AUTH_KEYS.ACCESS_TOKEN);
|
Storage.remove(AUTH_KEYS.ACCESS_TOKEN);
|
||||||
Storage.remove(AUTH_KEYS.REFRESH_TOKEN);
|
Storage.remove(AUTH_KEYS.REFRESH_TOKEN);
|
||||||
Storage.sessionRemove(AUTH_KEYS.ACCESS_TOKEN);
|
Storage.sessionRemove(AUTH_KEYS.ACCESS_TOKEN);
|
||||||
Storage.sessionRemove(AUTH_KEYS.REFRESH_TOKEN);
|
Storage.sessionRemove(AUTH_KEYS.REFRESH_TOKEN);
|
||||||
// 不清除记住我设置,保留用户偏好
|
},
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
getRememberMe(): boolean {
|
||||||
* 获取"记住我"状态
|
|
||||||
* @returns 是否记住我
|
|
||||||
*/
|
|
||||||
static getRememberMe(): boolean {
|
|
||||||
return Storage.get<boolean>(AUTH_KEYS.REMEMBER_ME, false);
|
return Storage.get<boolean>(AUTH_KEYS.REMEMBER_ME, false);
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import axios, { type InternalAxiosRequestConfig, type AxiosResponse } from "axio
|
|||||||
import qs from "qs";
|
import qs from "qs";
|
||||||
import { useUserStoreHook } from "@/store/modules/user.store";
|
import { useUserStoreHook } from "@/store/modules/user.store";
|
||||||
import { ResultEnum } from "@/enums/api/result.enum";
|
import { ResultEnum } from "@/enums/api/result.enum";
|
||||||
import { Auth } from "@/utils/auth";
|
import { AuthStorage } from "@/utils/auth";
|
||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -20,7 +20,7 @@ const httpRequest = axios.create({
|
|||||||
*/
|
*/
|
||||||
httpRequest.interceptors.request.use(
|
httpRequest.interceptors.request.use(
|
||||||
(config: InternalAxiosRequestConfig) => {
|
(config: InternalAxiosRequestConfig) => {
|
||||||
const accessToken = Auth.getAccessToken();
|
const accessToken = AuthStorage.getAccessToken();
|
||||||
|
|
||||||
// 如果 Authorization 设置为 no-auth,则不携带 Token
|
// 如果 Authorization 设置为 no-auth,则不携带 Token
|
||||||
if (config.headers.Authorization !== "no-auth" && accessToken) {
|
if (config.headers.Authorization !== "no-auth" && accessToken) {
|
||||||
@@ -104,7 +104,7 @@ async function refreshTokenAndRetry(config: InternalAxiosRequestConfig): Promise
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// 封装需要重试的请求
|
// 封装需要重试的请求
|
||||||
const retryRequest = () => {
|
const retryRequest = () => {
|
||||||
const newToken = Auth.getAccessToken();
|
const newToken = AuthStorage.getAccessToken();
|
||||||
if (newToken && config.headers) {
|
if (newToken && config.headers) {
|
||||||
config.headers.Authorization = `Bearer ${newToken}`;
|
config.headers.Authorization = `Bearer ${newToken}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ import AuthAPI, { type LoginFormData } from "@/api/auth.api";
|
|||||||
import router from "@/router";
|
import router from "@/router";
|
||||||
import { useUserStore } from "@/store";
|
import { useUserStore } from "@/store";
|
||||||
import CommonWrapper from "@/components/CommonWrapper/index.vue";
|
import CommonWrapper from "@/components/CommonWrapper/index.vue";
|
||||||
import { Auth } from "@/utils/auth";
|
import { AuthStorage } from "@/utils/auth";
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
@@ -130,7 +130,7 @@ const isCapsLock = ref(false);
|
|||||||
// 验证码图片Base64字符串
|
// 验证码图片Base64字符串
|
||||||
const captchaBase64 = ref();
|
const captchaBase64 = ref();
|
||||||
// 记住我
|
// 记住我
|
||||||
const rememberMe = Auth.getRememberMe();
|
const rememberMe = AuthStorage.getRememberMe();
|
||||||
|
|
||||||
const loginFormData = ref<LoginFormData>({
|
const loginFormData = ref<LoginFormData>({
|
||||||
username: "admin",
|
username: "admin",
|
||||||
|
|||||||
Reference in New Issue
Block a user