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

如何在Next.js中间件中使用Firebase User实现路由保护并解决useContext报错

问题原因分析

你遇到的错误是因为Next.js 中间件的运行环境不支持 React Context:

  • React 的 useContext 只能在 React 函数组件或自定义 Hooks 内部调用,而中间件运行在 Node.js/边缘 Runtime 中,不属于 React 组件树环境,直接访问 Context 会触发 Cannot read properties of null (reading 'useContext') 错误。
  • 你在页面组件中能正常获取 User 对象,是因为页面属于 React 组件树,符合 Context 的使用场景。

要在中间件中实现登录状态校验,需要将 Firebase 的认证信息存储到 HttpOnly Cookie 中,然后在中间件里通过 Firebase Admin SDK 验证 Cookie 有效性,以此判断用户登录状态。

在你的登录逻辑(比如 Context 的 login 函数)中,获取 Firebase ID Token 并调用 API 路由将其转为 Session Cookie:

// 示例:Context 中的登录函数
import { signInWithEmailAndPassword, getAuth } from "firebase/auth";

const login = async (email, password) => {
  const auth = getAuth();
  const userCredential = await signInWithEmailAndPassword(auth, email, password);
  const idToken = await userCredential.user.getIdToken();

  // 调用 API 路由存储 Session Cookie
  await fetch("/api/set-session-cookie", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ idToken }),
  });
};

在 pages/api/set-session-cookie.js 中,用 Firebase Admin SDK 将 ID Token 转为 Session Cookie:

import { initializeApp, cert } from "firebase-admin/app";
import { getAuth } from "firebase-admin/auth";

// 初始化 Firebase Admin(提前下载服务账号密钥放在项目根目录)
if (!initializeApp.length) {
  initializeApp({
    credential: cert("./service-account-key.json"),
  });
}
const adminAuth = getAuth();

export default async function handler(req, res) {
  const { idToken } = req.body;
  const expiresIn = 60 * 60 * 24 * 5 * 1000; // Cookie 有效期5天

  const sessionCookie = await adminAuth.createSessionCookie(idToken, { expiresIn });

  // 设置 HttpOnly、Secure Cookie(生产环境开启 Secure)
  res.setHeader(
    "Set-Cookie",
    `session=${sessionCookie}; HttpOnly; Path=/; Max-Age=${expiresIn / 1000}; ${
      process.env.NODE_ENV === "production" ? "Secure;" : ""
    }`
  );
  res.status(200).json({ success: true });
}

修改 middleware.js,读取并验证 Session Cookie,根据状态处理重定向:

import { NextResponse } from "next/server";
import { getAuth } from "firebase-admin/auth";
import { initializeApp, cert } from "firebase-admin/app";

// 初始化 Firebase Admin
if (!initializeApp.length) {
  initializeApp({
    credential: cert("./service-account-key.json"),
  });
}
const adminAuth = getAuth();

export async function middleware(req) {
  const sessionCookie = req.cookies.get("session")?.value;
  let isLoggedIn = false;

  if (sessionCookie) {
    try {
      // 验证 Session Cookie 有效性
      await adminAuth.verifySessionCookie(sessionCookie, true);
      isLoggedIn = true;
    } catch (err) {
      // Cookie 无效,清除并跳转到登录页
      return NextResponse.redirect(new URL("/login", req.url)).clearCookie("session");
    }
  }

  // 根据登录状态处理重定向
  const isLoginPage = req.nextUrl.pathname === "/login";
  const isProtectedRoute = req.nextUrl.pathname.startsWith("/dashboard");

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

  if (isLoggedIn && isLoginPage) {
    return NextResponse.redirect(new URL("/dashboard", req.url));
  }

  return NextResponse.next();
}

// 指定中间件生效的路由
export const config = {
  matcher: ["/", "/login", "/dashboard/:path*"],
};

内容的提问来源于stack exchange,提问作者Sky Lurk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 04:50:25