Next生产环境middleware.ts拦截失效

2024-08 1

在next的middleware.ts中,判断当前用户是否有权限进入指定页面时,比如/middleware/home页面,用的是next-auth/jwt中的getToken,现在遇到一种情况,明明获取的token是null了,但是还是渲染没权限的页面,具体代码如下:

import { getToken } from "next-auth/jwt";
import { type NextRequest, NextResponse } from "next/server";
import { PATHS } from "@/constants";

export async function middleware(req: NextRequest) {
  const secret = process.env.AUTH_SECRET;
  try {
    // 确保环境变量已定义
    if (!secret) {
      throw new Error("AUTH_SECRET is not defined");
    }

    const token = await getToken({
      req,
      secret,
      salt:
        process.env.NODE_ENV === "production"
          ? "__Secure-authjs.session-token"
          : "authjs.session-token",
      cookieName:
        process.env.NODE_ENV === "production"
          ? "__Secure-authjs.session-token"
          : "authjs.session-token",
    });
    const { pathname } = req.nextUrl;

    const redirectAuthSignUrl = new URL(PATHS.AUTH_SIGN_IN, req.url);

    if (!token) {
      return NextResponse.redirect(redirectAuthSignUrl, { status: 302 });
    }

    // 只有管理员才能访问 /admin 路径
    if (pathname.startsWith(PATHS.ADMIN_HOME) && token.role !== "admin") {
      return NextResponse.redirect(new URL("/403", req.url));
    }

    // 放行其他请求
    return NextResponse.next();
  } catch (e) {
    console.log("middleware报错", e);
    console.log("middleware process.env", process.env);
  }
}

export const config = {
  matcher: ["/middleware/:path*"],
};

后来经过一步步排查,发现在请求该页面的时候,根本没有走middleware.ts的逻辑,而是直接请求该页面的资源,请求结束后才触发middleware.ts的逻辑,但此时页面已经不会跳转。
之后一直卡在为什么该页面不会走middleware.ts的逻辑这一步,多次google之后,了解到不是所有页面都会经过middleware.ts的逻辑的,Next在执行中间件得到时候,有自己的一套逻辑,对于静态页面,会直接返回,对于动态页面,才会走中间件的逻辑。
此时再回去排查该页面的代码,发现该页面的代码非常简单,如下所示:

import React from "react";

export default function AdminHomePage() {
  return <span>hello world</span>;
}

由于代码非常简单,且没有后端交互,这就导致当前路由的页面被Next解析为静态页面,从而避开了中间件的逻辑,因此,也就导致即使是中间件里getToken为null了,该页面也不会进行跳转。
修复的逻辑非常简单,将给页面强制为服务端渲染即可:

import React from "react";

export const dynamic = "force-dynamic";

export default function AdminHomePage() {
  return <span>hello world</span>;
}

上述代码中,加了一行export const dynamic = "force-dynamic";,这一行代码告诉Next,当前页面需要动态渲染,动态渲染的页面必定会经过中间件的逻辑。

  • Next