Next.js 如何统一检查用户登录状态及处理受保护路由?
统一处理Next.js的用户登录校验与受保护路由重定向
方案一:封装公共的getServerSideProps函数
把重复的校验逻辑抽成可复用的函数,分两种场景使用:需要登录才能访问的页面(受保护路由),以及不需要强制登录但要获取用户信息的页面。
1. 封装基础用户校验函数
新建utils/auth.js文件,把获取token、用户信息的逻辑抽出来:
// utils/auth.js export async function getAuthenticatedUser(context) { const token = await getToken(context); if (!token) { return { user: null, token: null }; } try { const profile = await getProfile(token); if (profile?.data?.user) { return { user: profile.data.user, token }; } } catch (err) { console.error('获取用户信息失败:', err); } return { user: null, token: null }; }
2. 封装受保护路由专用校验函数
针对必须登录才能进入的页面,写个强制登录的函数,未登录直接跳转到登录页:
// utils/auth.js export async function requireAuth(context) { const { user, token } = await getAuthenticatedUser(context); if (!user) { return { redirect: { destination: '/login', permanent: false, }, }; } return { props: { user, token }, }; }
3. 封装非强制登录的校验函数
对于首页这类不需要登录也能访问,但要显示用户状态的页面,用这个函数:
// utils/auth.js export async function getAuthProps(context) { const { user, token } = await getAuthenticatedUser(context); return { props: { user, token }, }; }
使用示例
- 受保护页面(比如
/dashboard):
// pages/dashboard.js import { requireAuth } from '../utils/auth'; export async function getServerSideProps(context) { return requireAuth(context); } export default function Dashboard({ user }) { return <div>欢迎,{user.name}</div>; }
- 非强制登录页面(比如首页
/):
// pages/index.js import { getAuthProps } from '../utils/auth'; export async function getServerSideProps(context) { return getAuthProps(context); } export default function Home({ user }) { return user ? <div>欢迎回来,{user.name}</div> : <div>请登录</div>; }
方案二:用自定义_app.js全局处理(Page Router)
如果所有页面都需要统一获取用户信息,可以在_app.js里通过getInitialProps全局处理,这样每个页面的props会自动带上用户信息,还能统一判断受保护路由。
// pages/_app.js import { getAuthenticatedUser } from '../utils/auth'; function MyApp({ Component, pageProps, user, token }) { return <Component {...pageProps} user={user} token={token} />; } MyApp.getInitialProps = async ({ Component, ctx }) => { const { user, token } = await getAuthenticatedUser(ctx); // 可以把受保护路由列表单独放到配置文件,方便后续维护 const isProtectedRoute = ['/dashboard', '/profile'].includes(ctx.pathname); if (isProtectedRoute && !user) { if (typeof window !== 'undefined') { window.location.href = '/login'; } else { ctx.res.writeHead(302, { Location: '/login' }); ctx.res.end(); } return {}; } const pageProps = Component.getInitialProps ? await Component.getInitialProps(ctx) : {}; return { pageProps, user, token }; }; export default MyApp;
注意:使用getInitialProps会关闭Next.js的静态优化,所有页面都会变为服务端渲染。如果部分页面需要静态生成,建议优先用方案一。
额外提示
- 把受保护路由列表单独放到配置文件,比如
config/routes.js,新增路由时直接修改配置即可:
// config/routes.js export const PROTECTED_ROUTES = ['/dashboard', '/profile', '/settings'];
- 记得处理token过期的情况:如果
getProfile调用失败,直接清除无效cookie再跳转到登录页。 - 如果使用Next.js 13+的App Router(App目录),用
middleware.js做路由守卫更高效:
// middleware.js import { NextResponse } from 'next/server'; import { getToken } from './utils/auth'; export async function middleware(request) { const path = request.nextUrl.pathname; const isProtectedRoute = ['/dashboard', '/profile'].includes(path); const token = await getToken(request); // 需适配App Router的token获取方式 const user = token ? await getProfile(token) : null; if (isProtectedRoute && !user) { return NextResponse.redirect(new URL('/login', request.url)); } return NextResponse.next(); } export const config = { matcher: ['/dashboard/:path*', '/profile/:path*'], // 指定需要拦截的路由 };
内容的提问来源于stack exchange,提问作者Ubeydullah Yılmaz
相关产品推荐
相关产品推荐

