diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index c8e890f..b35f8ee 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -154,7 +154,7 @@ function notifyBackendUnavailable(message: string) { } apiClient.interceptors.request.use((config) => { - const token = localStorage.getItem("live-recorder-token"); + const token = localStorage.getItem("live-recorder-token") ?? sessionStorage.getItem("live-recorder-token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } @@ -172,6 +172,8 @@ apiClient.interceptors.response.use( markBackendAvailable(); localStorage.removeItem("live-recorder-token"); localStorage.removeItem("live-recorder-user"); + sessionStorage.removeItem("live-recorder-token"); + sessionStorage.removeItem("live-recorder-user"); if (window.location.pathname !== "/login") { window.location.href = "/login"; diff --git a/frontend/src/stores/auth.ts b/frontend/src/stores/auth.ts index a369347..f682282 100644 --- a/frontend/src/stores/auth.ts +++ b/frontend/src/stores/auth.ts @@ -6,38 +6,51 @@ import type { AuthenticatedUser, LoginResponse } from "@/types"; const USER_STORAGE_KEY = "live-recorder-user"; const TOKEN_STORAGE_KEY = "live-recorder-token"; +function readStoredValue(key: string) { + return localStorage.getItem(key) ?? sessionStorage.getItem(key); +} + +function clearStoredSession() { + [localStorage, sessionStorage].forEach((storage) => { + storage.removeItem(TOKEN_STORAGE_KEY); + storage.removeItem(USER_STORAGE_KEY); + }); +} + export const useAuthStore = defineStore("auth", () => { - const token = ref(localStorage.getItem(TOKEN_STORAGE_KEY) ?? ""); + const token = ref(readStoredValue(TOKEN_STORAGE_KEY) ?? ""); const user = ref( (() => { - const raw = localStorage.getItem(USER_STORAGE_KEY); + const raw = readStoredValue(USER_STORAGE_KEY); return raw ? (JSON.parse(raw) as AuthenticatedUser) : null; })() ); const isAuthenticated = computed(() => Boolean(token.value)); - function persistSession(session: LoginResponse) { + function persistSession(session: LoginResponse, rememberMe: boolean) { token.value = session.token; user.value = session.user; - localStorage.setItem(TOKEN_STORAGE_KEY, session.token); - localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(session.user)); + clearStoredSession(); + const storage = rememberMe ? localStorage : sessionStorage; + storage.setItem(TOKEN_STORAGE_KEY, session.token); + storage.setItem(USER_STORAGE_KEY, JSON.stringify(session.user)); } function clearSession() { token.value = ""; user.value = null; - localStorage.removeItem(TOKEN_STORAGE_KEY); - localStorage.removeItem(USER_STORAGE_KEY); + clearStoredSession(); } - async function login(username: string, password: string) { + async function login(username: string, password: string, rememberMe: boolean) { const { data } = await apiClient.post("/auth/login", { username, - password + password, + rememberMe }); - persistSession(data); + persistSession(data, rememberMe); return data; } @@ -54,6 +67,7 @@ export const useAuthStore = defineStore("auth", () => { currentPassword, newPassword }); + clearSession(); } return { diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue index 5c0d3f6..5fcb738 100644 --- a/frontend/src/views/LoginView.vue +++ b/frontend/src/views/LoginView.vue @@ -16,7 +16,8 @@ const { themeMode } = useUiPreferences(); const form = reactive({ username: "admin", - password: "" + password: "", + rememberMe: false }); const currentThemeIcon = computed(() => { @@ -35,7 +36,7 @@ async function handleLogin() { loading.value = true; try { - await authStore.login(form.username, form.password); + await authStore.login(form.username, form.password, form.rememberMe); ElMessage.success("登录成功"); await router.push({ name: "dashboard" }); } catch (error) { @@ -94,10 +95,12 @@ async function handleLogin() { :description="backendMessage" /> -