diff --git a/apps/jingrow/frontend/src/app/main.ts b/apps/jingrow/frontend/src/app/main.ts index 1d8b71a..1a95c68 100644 --- a/apps/jingrow/frontend/src/app/main.ts +++ b/apps/jingrow/frontend/src/app/main.ts @@ -21,6 +21,9 @@ app.use(createPinia()) app.use(router) app.use(naive) +// 初始化fetch拦截器(需要在pinia初始化之后) +import '../shared/utils/fetchInterceptor' + // 初始化认证状态 import { useAuthStore } from '../shared/stores/auth' const authStore = useAuthStore() diff --git a/apps/jingrow/frontend/src/app/router/index.ts b/apps/jingrow/frontend/src/app/router/index.ts index 21479fc..7e82b34 100644 --- a/apps/jingrow/frontend/src/app/router/index.ts +++ b/apps/jingrow/frontend/src/app/router/index.ts @@ -183,7 +183,7 @@ const router = createRouter({ router.beforeEach(async (to, _from, next) => { const authStore = useAuthStore() - // 初始化认证状态 + // 初始化认证状态(内部会检查cookie是否过期) if (!authStore.isAuthenticated) { await authStore.initAuth() } diff --git a/apps/jingrow/frontend/src/shared/api/auth.ts b/apps/jingrow/frontend/src/shared/api/auth.ts index db3b981..a7aa533 100644 --- a/apps/jingrow/frontend/src/shared/api/auth.ts +++ b/apps/jingrow/frontend/src/shared/api/auth.ts @@ -27,6 +27,14 @@ export function getSessionCookie(): string | null { return cookies.get('sid') } +// 检查cookie是否过期(通过检查session cookie是否存在) +export function isCookieExpired(): boolean { + const sessionCookie = getSessionCookie() + const sessionUser = getSessionUser() + // 如果session cookie或user_id不存在,认为cookie已过期 + return !sessionCookie || !sessionUser +} + export const loginApi = async (username: string, password: string): Promise => { const response = await fetch(`/jingrow/login`, { method: 'POST', @@ -67,6 +75,13 @@ export const getUserInfoApi = async (): Promise => { }) if (!response.ok) { + // 401或403表示cookie过期或未授权 + if (response.status === 401 || response.status === 403) { + const error = new Error('Cookie已过期,请重新登录') + // @ts-ignore + error.status = response.status + throw error + } const errorData = await response.json().catch(() => ({})) throw new Error(errorData.detail || errorData.message || '获取用户信息失败') } diff --git a/apps/jingrow/frontend/src/shared/stores/auth.ts b/apps/jingrow/frontend/src/shared/stores/auth.ts index a75639c..1b5b4af 100644 --- a/apps/jingrow/frontend/src/shared/stores/auth.ts +++ b/apps/jingrow/frontend/src/shared/stores/auth.ts @@ -1,6 +1,6 @@ import { defineStore } from 'pinia' import { ref, computed } from 'vue' -import { loginApi, getUserInfoApi, logoutApi, getSessionUser } from '../api/auth' +import { loginApi, getUserInfoApi, logoutApi, getSessionUser, isCookieExpired } from '../api/auth' export interface User { id: string @@ -59,6 +59,15 @@ export const useAuthStore = defineStore('auth', () => { } const initAuth = async () => { + // 首先检查Cookie是否过期 + if (isCookieExpired()) { + // Cookie已过期,清除本地状态 + if (isAuthenticated.value) { + await logout() + } + return + } + // 首先检查Cookie中的session const sessionUser = getSessionUser() if (sessionUser) { @@ -72,8 +81,12 @@ export const useAuthStore = defineStore('auth', () => { localStorage.setItem('jingrow_user', JSON.stringify(userInfo)) localStorage.setItem('jingrow_authenticated', 'true') return - } catch (error) { + } catch (error: any) { console.error('验证用户信息失败:', error) + // 如果是401/403错误,说明cookie已过期 + if (error.status === 401 || error.status === 403 || error.message?.includes('过期')) { + await logout() + } } } @@ -89,9 +102,14 @@ export const useAuthStore = defineStore('auth', () => { // 验证用户信息是否仍然有效 const userInfo = await getUserInfoApi() user.value = userInfo - } catch (error) { + } catch (error: any) { console.error('验证用户信息失败:', error) - logout() + // 如果是401/403错误,说明cookie已过期 + if (error.status === 401 || error.status === 403 || error.message?.includes('过期')) { + await logout() + } else { + logout() + } } } } @@ -103,8 +121,12 @@ export const useAuthStore = defineStore('auth', () => { const userInfo = await getUserInfoApi() user.value = userInfo localStorage.setItem('jingrow_user', JSON.stringify(userInfo)) - } catch (error) { + } catch (error: any) { console.error('更新用户信息失败:', error) + // 如果是401/403错误,说明cookie已过期 + if (error.status === 401 || error.status === 403 || error.message?.includes('过期')) { + await logout() + } } } diff --git a/apps/jingrow/frontend/src/shared/utils/fetchInterceptor.ts b/apps/jingrow/frontend/src/shared/utils/fetchInterceptor.ts new file mode 100644 index 0000000..f488aed --- /dev/null +++ b/apps/jingrow/frontend/src/shared/utils/fetchInterceptor.ts @@ -0,0 +1,37 @@ +// 保存原始的fetch函数 +const originalFetch = window.fetch + +// 包装fetch函数,添加401/403错误处理 +window.fetch = async function(...args: Parameters): Promise { + const response = await originalFetch(...args) + + // 检查响应状态码(仅在401/403时处理) + if (response.status === 401 || response.status === 403) { + // 延迟导入,确保pinia已初始化 + try { + const { useAuthStore } = await import('../stores/auth') + const { default: router } = await import('../../app/router') + const authStore = useAuthStore() + + // 如果用户已登录,执行登出操作 + if (authStore.isLoggedIn) { + console.warn('检测到401/403错误,Cookie已过期,自动退出登录') + await authStore.logout() + + // 跳转到登录页(避免重复跳转) + if (router.currentRoute.value.path !== '/login') { + router.push('/login') + } + } + } catch (error) { + // 如果store还未初始化,忽略错误(应用启动阶段) + // 这不会影响正常的API调用 + } + } + + return response +} + +// 导出原始fetch(如果需要的话) +export { originalFetch as originalFetch } +