You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 06:33:30