auto logout on cookie expiration
This commit is contained in:
parent
3ce344f969
commit
19bd8ff104
@ -21,6 +21,9 @@ app.use(createPinia())
|
|||||||
app.use(router)
|
app.use(router)
|
||||||
app.use(naive)
|
app.use(naive)
|
||||||
|
|
||||||
|
// 初始化fetch拦截器(需要在pinia初始化之后)
|
||||||
|
import '../shared/utils/fetchInterceptor'
|
||||||
|
|
||||||
// 初始化认证状态
|
// 初始化认证状态
|
||||||
import { useAuthStore } from '../shared/stores/auth'
|
import { useAuthStore } from '../shared/stores/auth'
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|||||||
@ -183,7 +183,7 @@ const router = createRouter({
|
|||||||
router.beforeEach(async (to, _from, next) => {
|
router.beforeEach(async (to, _from, next) => {
|
||||||
const authStore = useAuthStore()
|
const authStore = useAuthStore()
|
||||||
|
|
||||||
// 初始化认证状态
|
// 初始化认证状态(内部会检查cookie是否过期)
|
||||||
if (!authStore.isAuthenticated) {
|
if (!authStore.isAuthenticated) {
|
||||||
await authStore.initAuth()
|
await authStore.initAuth()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,14 @@ export function getSessionCookie(): string | null {
|
|||||||
return cookies.get('sid')
|
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> => {
|
export const loginApi = async (username: string, password: string): Promise<LoginResponse> => {
|
||||||
const response = await fetch(`/jingrow/login`, {
|
const response = await fetch(`/jingrow/login`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -67,6 +75,13 @@ export const getUserInfoApi = async (): Promise<UserInfo> => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
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(() => ({}))
|
const errorData = await response.json().catch(() => ({}))
|
||||||
throw new Error(errorData.detail || errorData.message || '获取用户信息失败')
|
throw new Error(errorData.detail || errorData.message || '获取用户信息失败')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref, computed } from 'vue'
|
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 {
|
export interface User {
|
||||||
id: string
|
id: string
|
||||||
@ -59,6 +59,15 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initAuth = async () => {
|
const initAuth = async () => {
|
||||||
|
// 首先检查Cookie是否过期
|
||||||
|
if (isCookieExpired()) {
|
||||||
|
// Cookie已过期,清除本地状态
|
||||||
|
if (isAuthenticated.value) {
|
||||||
|
await logout()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 首先检查Cookie中的session
|
// 首先检查Cookie中的session
|
||||||
const sessionUser = getSessionUser()
|
const sessionUser = getSessionUser()
|
||||||
if (sessionUser) {
|
if (sessionUser) {
|
||||||
@ -72,8 +81,12 @@ export const useAuthStore = defineStore('auth', () => {
|
|||||||
localStorage.setItem('jingrow_user', JSON.stringify(userInfo))
|
localStorage.setItem('jingrow_user', JSON.stringify(userInfo))
|
||||||
localStorage.setItem('jingrow_authenticated', 'true')
|
localStorage.setItem('jingrow_authenticated', 'true')
|
||||||
return
|
return
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('验证用户信息失败:', error)
|
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()
|
const userInfo = await getUserInfoApi()
|
||||||
user.value = userInfo
|
user.value = userInfo
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('验证用户信息失败:', error)
|
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()
|
const userInfo = await getUserInfoApi()
|
||||||
user.value = userInfo
|
user.value = userInfo
|
||||||
localStorage.setItem('jingrow_user', JSON.stringify(userInfo))
|
localStorage.setItem('jingrow_user', JSON.stringify(userInfo))
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error('更新用户信息失败:', error)
|
console.error('更新用户信息失败:', error)
|
||||||
|
// 如果是401/403错误,说明cookie已过期
|
||||||
|
if (error.status === 401 || error.status === 403 || error.message?.includes('过期')) {
|
||||||
|
await logout()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
37
apps/jingrow/frontend/src/shared/utils/fetchInterceptor.ts
Normal file
37
apps/jingrow/frontend/src/shared/utils/fetchInterceptor.ts
Normal 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 }
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user