在 Proxy(原 Middleware) 实现 身份验证

在 Next.js 16 中,推荐使用 proxy(原 middleware) 来统一处理登录态校验与访问控制。由于所有请求都会经过这一层,非常适合做集中式鉴权与重定向。

1. 核心思路

  1. 放行公共资源, 例如:登录页(/login), 首页(/), 这些页面无需登录即可访问,应直接放行。
  2. 乐观检查(Optimistic Check), 优先检查 Cookie 中是否存在登录凭证:sb-access-token, sb-refresh-token
  3. 服务端验证用户状态
  4. 放行API请求和静态资源

2. 示例实现

// proxy.ts
import { NextRequest, NextResponse } from "next/server";

import { createSupabaseServerClient } from "./lib/supabase/server";

// 指定公开路由和保护的路由
const publicRoutes = ["/login", "/courses", "/"];

export default async function proxy(req: NextRequest) {
  // 检查路由是否是公开
  const path = req.nextUrl.pathname;
  const isPublicRoute = publicRoutes.includes(path);

  if (isPublicRoute) {
    return NextResponse.next();
  }

  // 先做“乐观检查”(是否有 token)
  const hasAuthCookie = req.cookies
    .getAll()
    .some((cookie) => cookie.name.startsWith("sb-") && cookie.name.endsWith("-auth-token"));

  if (!hasAuthCookie) {
    return NextResponse.redirect(new URL("/login", req.url));
  }

  // 检查用户状态
  const {
    data: { user },
  } = await (await createSupabaseServerClient()).auth.getUser();

  // 未授权用户重定向
  if (!user) {
    return NextResponse.redirect(new URL("/login", req.nextUrl));
  }
  return NextResponse.next();
}

// Routes Proxy should not run on
export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico|.*\\.png$).*)"],
};