Next.js App Router中服务端请求后跳转登录页的通用实现
Next.js App Router 通用服务端认证与自动跳转实现
概念性错误排查
先明确几个关键问题,避免踩坑:
- 硬编码redirect完全没必要:服务端可以自动获取当前请求路径,不需要手动写死
/profile这类路径。 - 服务端跳转必须用
redirect函数:App Router的服务端组件里不能用客户端的useRouter做跳转,必须用Next.js提供的redirect函数,且要在组件渲染前的顶级await中调用。 - 认证逻辑要放在服务端:不能在客户端组件里处理Cookie认证和401跳转,否则会泄露未授权页面的结构,也无法有效拦截未登录请求。
通用实现方案
方案1:封装通用服务端认证函数(适合灵活控制单页逻辑)
写一个可复用的认证函数,在需要认证的页面中直接调用,自动处理401跳转并携带当前路径:
// app/utils/auth.ts import { redirect } from 'next/navigation'; import { cookies, headers } from 'next/headers'; export async function getUserWithAuth() { // 获取服务端Cookie const authCookie = cookies().get('auth-token')?.value; // 调用后端认证接口 const res = await fetch('https://你的后端域名/user', { headers: { Cookie: authCookie ? `auth-token=${authCookie}` : '' }, cache: 'no-store' // 禁用缓存,确保每次请求都验证 }); if (res.status === 401) { // 获取当前页面的完整URL(包含路径和查询参数) const currentUrl = headers().get('x-nextjs-url') || '/'; // 编码路径,避免特殊字符破坏URL结构 const encodedRedirect = encodeURIComponent(currentUrl); // 跳转到登录页并携带redirect参数 redirect(`/login?redirect=${encodedRedirect}`); } if (!res.ok) { throw new Error('获取用户数据失败'); } return res.json(); }
在需要认证的页面中直接使用:
// app/profile/page.tsx import { getUserWithAuth } from '@/app/utils/auth'; export default async function ProfilePage() { // 顶级await调用认证函数,自动处理跳转 const user = await getUserWithAuth(); return ( <div> <h1>个人中心</h1> <p>欢迎你,{user.nickname}</p> </div> ); }
方案2:用Middleware统一拦截(适合全局批量路由)
如果大部分页面都需要认证,用Next.js中间件可以统一处理,不用每个页面都写代码:
// middleware.ts import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; export async function middleware(request: NextRequest) { // 排除不需要认证的路由(登录页、公共页面等) const publicPaths = ['/login', '/register', '/public']; if (publicPaths.some(path => request.nextUrl.pathname.startsWith(path))) { return NextResponse.next(); } // 获取认证Cookie const authCookie = request.cookies.get('auth-token')?.value; // 无Cookie直接跳转登录 if (!authCookie) { const encodedRedirect = encodeURIComponent(request.nextUrl.href); return NextResponse.redirect(new URL(`/login?redirect=${encodedRedirect}`, request.url)); } // 调用后端验证Cookie有效性 const validateRes = await fetch('https://你的后端域名/validate-token', { headers: { Cookie: `auth-token=${authCookie}` }, cache: 'no-store' }); // 验证失败跳转登录 if (validateRes.status === 401) { const encodedRedirect = encodeURIComponent(request.nextUrl.href); return NextResponse.redirect(new URL(`/login?redirect=${encodedRedirect}`, request.url)); } // 认证通过,放行请求 return NextResponse.next(); } // 指定需要拦截的路由(所有需要认证的页面) export const config = { matcher: ['/profile/:path*', '/dashboard/:path*', '/settings/:path*'], };
注意事项
- 调用后端接口时要加
cache: 'no-store',避免Next.js缓存认证结果,导致用户登出后仍能访问页面。 - 必须用
encodeURIComponent编码redirect参数,防止路径中的?、&等特殊字符破坏URL结构。 - 中间件中调用后端接口要注意性能,避免频繁请求,可以考虑加入短时间缓存(比如用Redis),但要确保认证状态实时更新。
内容的提问来源于stack exchange,提问作者Georg Windhaber
相关产品推荐
相关产品推荐

