auto logout on cookie expiration

This commit is contained in:
jingrow 2025-11-20 09:29:13 +08:00
parent 3ce344f969
commit 19bd8ff104
5 changed files with 83 additions and 6 deletions

View File

@ -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()

View File

@ -183,7 +183,7 @@ const router = createRouter({
router.beforeEach(async (to, _from, next) => {
const authStore = useAuthStore()
// 初始化认证状态
// 初始化认证状态内部会检查cookie是否过期
if (!authStore.isAuthenticated) {
await authStore.initAuth()
}

View File

@ -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<LoginResponse> => {
const response = await fetch(`/jingrow/login`, {
method: 'POST',
@ -67,6 +75,13 @@ export const getUserInfoApi = async (): Promise<UserInfo> => {
})
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 || '获取用户信息失败')
}

View File

@ -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()
}
}
}

View File

@ -0,0 +1,37 @@
// 保存原始的fetch函数
const originalFetch = window.fetch
// 包装fetch函数添加401/403错误处理
window.fetch = async function(...args: Parameters<typeof fetch>): Promise<Response> {
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 }