diff --git a/src/components/TokenRefreshManager.tsx b/src/components/TokenRefreshManager.tsx index 375171e..21f3cd2 100644 --- a/src/components/TokenRefreshManager.tsx +++ b/src/components/TokenRefreshManager.tsx @@ -2,7 +2,7 @@ import { useEffect } from 'react'; -import { getAuthInfoFromBrowserCookie } from '@/lib/auth'; +import { getAuthInfoFromBrowserCookie, clearAuthCookie } from '@/lib/auth'; /** * Token 自动刷新管理器 @@ -50,8 +50,18 @@ export function TokenRefreshManager() { } else { console.error('[Token] Refresh failed:', response.status); - // 刷新失败,跳转登录 + // 刷新失败,先登出再跳转登录 if (response.status === 401 || response.status === 403) { + try { + await window.fetch('/api/logout', { + method: 'POST', + credentials: 'include', + }); + } catch (error) { + console.error('[Token] Logout error:', error); + // 登出失败时清除前端cookie + clearAuthCookie(); + } window.location.href = `/login?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`; } return false; @@ -80,7 +90,17 @@ export function TokenRefreshManager() { // Refresh Token 已过期 if (now >= authInfo.refreshExpires) { console.log('[Token] Refresh token expired, redirecting to login'); - window.location.href = `/login?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`; + // 先登出再跳转登录 + window.fetch('/api/logout', { + method: 'POST', + credentials: 'include', + }).catch(error => { + console.error('[Token] Logout error:', error); + // 登出失败时清除前端cookie + clearAuthCookie(); + }).finally(() => { + window.location.href = `/login?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`; + }); return false; } @@ -132,9 +152,19 @@ export function TokenRefreshManager() { // 刷新成功,重试原请求(仅此一次) response = await originalFetch(input, init); - // 如果重试后仍然是 401,说明有问题,跳转登录 + // 如果重试后仍然是 401,说明有问题,先登出再跳转登录 if (response.status === 401) { console.error('[Token] Still 401 after refresh, redirecting to login'); + try { + await originalFetch('/api/logout', { + method: 'POST', + credentials: 'include', + }); + } catch (error) { + console.error('[Token] Logout error:', error); + // 登出失败时清除前端cookie + clearAuthCookie(); + } window.location.href = `/login?redirect=${encodeURIComponent(window.location.pathname + window.location.search)}`; } } diff --git a/src/lib/auth.ts b/src/lib/auth.ts index f4b31b9..aff1762 100644 --- a/src/lib/auth.ts +++ b/src/lib/auth.ts @@ -111,3 +111,19 @@ export function getAuthInfoFromBrowserCookie(): AuthInfo | null { return null; } } + +// 清除浏览器中的认证cookie (客户端使用) +export function clearAuthCookie(): void { + if (typeof window === 'undefined') { + return; + } + + try { + // 清除 auth cookie,设置过期时间为过去 + document.cookie = 'auth=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax'; + // 如果有其他域名或路径的cookie,也尝试清除 + document.cookie = 'auth=; path=/; domain=' + window.location.hostname + '; expires=Thu, 01 Jan 1970 00:00:00 GMT; SameSite=Lax'; + } catch (error) { + console.error('[Auth] Failed to clear cookie:', error); + } +} diff --git a/src/lib/db.client.ts b/src/lib/db.client.ts index 3703572..bd2b532 100644 --- a/src/lib/db.client.ts +++ b/src/lib/db.client.ts @@ -14,7 +14,7 @@ * 如后续需要在客户端读取收藏等其它数据,可按同样方式在此文件中补充实现。 */ -import { getAuthInfoFromBrowserCookie } from './auth'; +import { getAuthInfoFromBrowserCookie, clearAuthCookie } from './auth'; import { DanmakuFilterConfig, EpisodeFilterConfig,SkipConfig } from './types'; // 全局错误触发函数 @@ -561,6 +561,8 @@ async function fetchWithAuth( }); } catch (error) { console.error('注销请求失败:', error); + // 登出失败时清除前端cookie + clearAuthCookie(); } const currentUrl = window.location.pathname + window.location.search; const loginUrl = new URL('/login', window.location.origin);