NextAuth多语言站点登录后重定向至原受保护页面的问题
解决方案
1. 拦截未登录访问,保存目标路径
在全局层面拦截未登录用户对受保护页面的访问,把当前带locale的完整路径作为参数传递到登录页。推荐用Next.js Middleware(Next.js 12+)实现全局控制:
// middleware.js import { getToken } from "next-auth/jwt"; import { NextResponse } from "next/server"; export async function middleware(req) { // 跳过登录页和静态资源 if (req.nextUrl.pathname.startsWith("/login") || req.nextUrl.pathname.startsWith("/_next")) { return NextResponse.next(); } // 验证用户登录状态 const token = await getToken({ req, secret: process.env.NEXTAUTH_SECRET }); if (!token) { // 编码当前访问路径,避免特殊字符解析错误 const callbackUrl = encodeURIComponent(req.nextUrl.href); return NextResponse.redirect(new URL(`/login?callbackUrl=${callbackUrl}`, req.url)); } return NextResponse.next(); }
如果用页面级的getServerSideProps,可以在每个受保护页面添加:
// 受保护页面的getServerSideProps export async function getServerSideProps(context) { const session = await getSession(context); if (!session) { // 拼接带locale的目标路径 const callbackUrl = `${context.locale}${context.resolvedUrl}`; return { redirect: { destination: `/login?callbackUrl=${encodeURIComponent(callbackUrl)}`, permanent: false, }, }; } return { props: {} }; }
2. 登录页面动态传入callbackUrl
在自定义登录页中,从URL参数里提取之前保存的目标路径,没有的话再用对应locale的默认路径:
// 登录页面组件 import { useRouter } from "next/router"; import { signIn } from "next-auth/react"; export default function LoginPage() { const router = useRouter(); const { callbackUrl } = router.query; // 根据当前locale生成默认跳转路径 const defaultCallbackUrl = router.locale === "id-ID" ? "/id-ID/app" : "/en-US/app"; // 优先使用URL参数里的目标路径,否则用默认值 const targetUrl = callbackUrl ? decodeURIComponent(callbackUrl) : defaultCallbackUrl; return ( <button onClick={() => signIn("google", { callbackUrl: targetUrl })} > 登录Google </button> ); }
3. 优化NextAuth的redirect回调(增强安全性)
原有的redirect逻辑可以保留,额外增加locale格式验证,避免非法路径跳转:
// [...nextauth].js export default NextAuth({ adapter: MongoDBAdapter(clientPromise), secret: process.env.NEXTAUTH_SECRET, providers: [ GoogleProvider({ clientId: process.env.GOOGLE_ID, clientSecret: process.env.GOOGLE_SECRET, }), ], callbacks: { async redirect({ url, baseUrl }) { // 处理带locale的相对路径 if (url.startsWith("/")) { const validLocales = ["id-ID", "en-US"]; const pathSegments = url.split("/"); // 验证路径是否包含合法locale前缀 if (pathSegments.length >= 2 && validLocales.includes(pathSegments[1])) { return `${baseUrl}${url}`; } // 非法路径跳转到对应locale的默认页 return `${baseUrl}/${baseUrl.locale || "en-US"}/app`; } // 允许同域的完整URL else if (new URL(url).origin === baseUrl) return url; // 其他情况跳转到默认页 return `${baseUrl}/${baseUrl.locale || "en-US"}/app`; } } })
关键注意事项
- 必须保证
callbackUrl包含完整的locale前缀(如/id-ID/app),否则Next.js的locale路由会自动跳转到默认语言,导致路径错误。 - 用
encodeURIComponent和decodeURIComponent处理URL参数,避免特殊字符导致的路径解析异常。 - Middleware是全局拦截的最优方案,无需在每个受保护页面重复编写登录验证逻辑。
内容的提问来源于stack exchange,提问作者Febry Aryo
相关产品推荐
相关产品推荐

