Next.js(App Dir)中如何用Next-Auth实现会话过期自动登出?
Next.js App Dir 实现无操作自动登出方案
先解决 Session Null 错误
任何读取会话数据的场景,必须先做空值校验,避免直接访问 null 属性:
服务器组件中(如页面、布局)
import { getServerSession } from 'next-auth/next'; import { authOptions } from '@/auth'; import { redirect } from 'next/navigation'; export default async function DashboardPage() { const session = await getServerSession(authOptions); if (!session) { redirect('/login'); } return <div>欢迎回来,{session.user?.name}</div>; }
客户端组件中
'use client'; import { useSession } from 'next-auth/react'; import { useRouter } from 'next/navigation'; import { useEffect } from 'react'; export default function Profile() { const { data: session } = useSession(); const router = useRouter(); useEffect(() => { if (!session) router.push('/login'); }, [session, router]); if (!session) return null; return <div>你的邮箱:{session.user.email}</div>; }
实现无操作自动登出核心逻辑
创建客户端组件 AutoLogout.tsx,监听用户交互并管理超时定时器:
'use client'; import { useEffect, useState } from 'react'; import { signOut } from '@/auth'; import { useSession } from 'next-auth/react'; // 自定义超时时间(示例为30分钟) const AUTO_LOGOUT_DURATION = 30 * 60 * 1000; export default function AutoLogout() { const { data: session } = useSession(); const [logoutTimer, setLogoutTimer] = useState<NodeJS.Timeout | null>(null); // 重置超时定时器 const resetTimer = () => { if (logoutTimer) clearTimeout(logoutTimer); const timer = setTimeout(async () => { await signOut({ redirect: true, callbackUrl: '/' }); }, AUTO_LOGOUT_DURATION); setLogoutTimer(timer); }; useEffect(() => { // 会话不存在时不初始化定时器 if (!session) return; // 监听用户交互事件 const userEvents = ['mousemove', 'keydown', 'scroll', 'touchstart']; userEvents.forEach(event => window.addEventListener(event, resetTimer)); // 初始化定时器 resetTimer(); // 清理函数:移除监听并销毁定时器 return () => { userEvents.forEach(event => window.removeEventListener(event, resetTimer)); if (logoutTimer) clearTimeout(logoutTimer); }; }, [session, logoutTimer]); return null; // 组件无需渲染内容 }
在根布局中引入自动登出组件
根布局默认是服务器组件,直接导入客户端组件即可:
// app/layout.tsx import AutoLogout from '@/components/AutoLogout'; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="zh-CN"> <body> {children} <AutoLogout /> </body> </html> ); }
说明之前方案的问题
- Layout 中重定向失败:App Dir 布局默认是服务器组件,无法监听客户端交互;若用服务器端
redirect,只能处理初始会话校验,无法应对会话过期后的客户端无操作场景,必须结合客户端组件实现自动登出。 - useEffect 循环问题:之前未正确清理定时器,或依赖项设置错误,导致会话变化后反复触发 useEffect。上述代码仅在会话存在时初始化定时器,且清理函数会移除监听并销毁定时器,彻底避免循环。
内容的提问来源于stack exchange,提问作者Nahuel Chaikh
相关产品推荐
相关产品推荐

