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

Next.js使用getServerSideProps访问Firebase报insufficient permissions错误

问题原因

你遇到的权限报错本质是getServerSideProps运行在Next.js服务端,而Firebase客户端SDK的登录态仅存在于用户浏览器中,服务端执行Firestore查询时没有携带任何用户身份信息,所以Firebase安全规则判定request.auth为null,触发权限拒绝。
纯React客户端应用所有数据请求都在浏览器执行,会自动携带当前登录的auth状态,所以不会遇到这类问题。


解决方案1:将会话数据获取移到客户端执行(门槛最低,适配现有逻辑)

这种方案不需要修改服务端逻辑,直接把数据请求从getServerSideProps移到页面组件的副作用里,等客户端Firebase auth状态加载完成后再发起请求:
修改你的[conversationid].tsx代码如下:

import React, { useEffect, useState } from "react";
import Head from "next/head";
import Navbar from "../../components/Navbar/Navbar";
import Footer from "../../components/Footer/Footer";
import Chat from "../../components/Chat/Chat";
import { getConversationData } from "../../services/firebase";
// 引入你的Firebase auth实例
import { auth } from "../../services/firebase";
import { onAuthStateChanged } from "firebase/auth";

function Page({ conversationid }) {
  const [conversationData, setConversationData] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [isNotFound, setIsNotFound] = useState(false);

  useEffect(() => {
    // 等待auth状态加载完成再请求数据
    const unsubscribe = onAuthStateChanged(auth, async (user) => {
      if (!user) {
        // 未登录可跳转至登录页
        window.location.href = '/login';
        return;
      }
      const response = await getConversationData(conversationid);
      if (response.success) {
        setConversationData(response.conversation);
      } else {
        setIsNotFound(true);
      }
      setIsLoading(false);
    });
    return () => unsubscribe();
  }, [conversationid]);

  if (isLoading) return <div>加载中...</div>;
  if (isNotFound) return <div>404 会话不存在</div>;

  return (
    <>
      <Head>
        <title>Chat</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Navbar page={"Profile"} />
      <Chat conversation={conversationData} />
      <Footer />
    </>
  );
}

// 仅通过getServerSideProps传递路由参数即可
export async function getServerSideProps(context) {
  return {
    props: {
      conversationid: context.params.conversationid,
    },
  };
}

export default Page;

解决方案2:服务端用Firebase Admin SDK验证身份(适合需要SSR渲染的场景)

如果你需要服务端渲染完整的页面内容,就要用Admin SDK来做服务端的身份校验:

  1. 首先在客户端登录成功后,把Firebase的ID Token写到cookie中,让服务端可以读取到身份凭证
  2. 服务端安装Firebase Admin SDK,初始化后读取cookie中的ID Token验证用户身份,再用Admin SDK查询Firestore(Admin SDK默认绕过安全规则,你可以自行在服务端做权限校验)

示例核心逻辑:
客户端登录成功后写入cookie:

// 客户端登录成功后执行
const idToken = await user.getIdToken();
document.cookie = `firebaseToken=${idToken}; path=/; max-age=86400`;

服务端getServerSideProps逻辑:

// 先初始化Firebase Admin SDK,注意服务账号密钥仅可在服务端使用,禁止泄露到前端
import admin from 'firebase-admin';
if (!admin.apps.length) {
  admin.initializeApp({
    credential: admin.credential.cert(你的服务账号密钥配置),
  });
}

export async function getServerSideProps(context) {
  // 从cookie中获取身份token
  const token = context.req.cookies.firebaseToken;
  if (!token) {
    return { redirect: { destination: '/login', permanent: false } };
  }
  try {
    // 验证token有效性
    const decodedToken = await admin.auth().verifyIdToken(token);
    const uid = decodedToken.uid;
    // 用Admin SDK查询Firestore
    const conversationDoc = await admin.firestore().collection('conversations').doc(context.params.conversationid).get();
    if (!conversationDoc.exists) {
      return { notFound: true };
    }
    // 自行补充权限校验:例如判断当前用户是否属于该会话
    const conversation = conversationDoc.data();
    if (!conversation.members.includes(uid)) {
      return { notFound: true };
    }
    return {
      props: {
        conversationData: conversation,
      },
    };
  } catch (e) {
    return { redirect: { destination: '/login', permanent: false } };
  }
}

方案选择建议

如果你的聊天页面不需要做SEO,优先选方案1,改动最小,和你之前写纯React的逻辑一致,也不需要处理服务端密钥等安全问题。如果需要SSR提升首屏加载速度或者做SEO,再选方案2。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 16:45:03