From 57c744eb17636e621630f2cfdab44f4ed5cfd53f Mon Sep 17 00:00:00 2001 From: wren <“porlong@qq.com”> Date: Wed, 6 May 2026 18:33:43 +0800 Subject: [PATCH] fix: redirect expired sessions to login --- app/components/auth/ClientAuthGuard.tsx | 17 +++++++++++++++-- app/root.tsx | 12 +++++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/app/components/auth/ClientAuthGuard.tsx b/app/components/auth/ClientAuthGuard.tsx index f65a715..b86fd37 100644 --- a/app/components/auth/ClientAuthGuard.tsx +++ b/app/components/auth/ClientAuthGuard.tsx @@ -31,6 +31,17 @@ export function ClientAuthGuard({ isPublicPath, frontendJWT, userInfo }: ClientA return; } + // 服务端如果已经拿不到有效 session,就不要再信任本地残留 token。 + // 否则页面会出现“模块全空了”,但又没有跳回登录页的假登录状态。 + if (!frontendJWT && !userInfo) { + console.warn('⚠️ [Auth Guard] 服务端会话已失效,清理本地登录态并跳转登录页'); + localStorage.removeItem('access_token'); + localStorage.removeItem('user_info'); + const redirectTo = `${location.pathname}${location.search}` || '/'; + navigate(`/login?expired=true&redirect=${encodeURIComponent(redirectTo)}`, { replace: true }); + return; + } + // 优先用服务端 session 回传的数据同步 localStorage。 // 不能只在本地没有 token 时才回填,否则本地残留旧 token 会导致: // - SSR 页面可打开(服务端 session 是新的) @@ -55,14 +66,16 @@ export function ClientAuthGuard({ isPublicPath, frontendJWT, userInfo }: ClientA console.log('🔒 [Auth Guard] 未认证,重定向到登录页'); // 保存当前路径,登录后可以跳转回来 - const redirectTo = location.pathname !== '/login' ? location.pathname : '/'; + const redirectTo = location.pathname !== '/login' + ? `${location.pathname}${location.search}` + : '/'; // 跳转到登录页,并传递重定向目标 navigate(`/login?redirect=${encodeURIComponent(redirectTo)}`, { replace: true }); } else { // console.log('✅ [Auth Guard] 已认证,允许访问'); } - }, [isPublicPath, navigate, location.pathname, frontendJWT, userInfo]); + }, [isPublicPath, navigate, location.pathname, location.search, frontendJWT, userInfo]); // 这个组件不渲染任何内容 return null; diff --git a/app/root.tsx b/app/root.tsx index eefe5c3..40e187e 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -300,12 +300,18 @@ export async function loader({ request }: LoaderFunctionArgs) { if (error instanceof Error && error.name === 'AuthenticationError') { console.warn("⚠️ [Root Loader] Token 过期,重定向到登录页"); // 保存当前路径,登录后可以跳转回来 - const redirectTo = pathname !== '/login' ? pathname : '/'; - return redirect(`/login?redirect=${encodeURIComponent(redirectTo)}`); + const redirectTo = pathname !== '/login' ? `${pathname}${url.search}` : '/'; + return redirect(`/login?expired=true&redirect=${encodeURIComponent(redirectTo)}`); } console.warn("⚠️ [Root Loader] 获取用户会话失败:", error); - // 保持默认值 'common' + + // 非公共页只要服务端会话初始化失败,就直接回登录页, + // 避免落入“角色=common + 菜单全空”的假登录状态。 + if (!isPublicPath) { + const redirectTo = pathname !== '/login' ? `${pathname}${url.search}` : '/'; + return redirect(`/login?expired=true&redirect=${encodeURIComponent(redirectTo)}`); + } } // 注意:认证检查和重定向已在 getUserSession() 中统一处理