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

Next.js 13.4.12 App Router下Dashboard路由保护及布局闪烁问题

Next.js 13.4.12 App Router + Firebase Auth 路由保护方案

1. 中间件实现路由保护(解决Edge runtime不兼容firebase-admin问题)

由于Next.js中间件运行在Edge runtime,无法使用依赖Node.js API的firebase-admin SDK,可改用Firebase Auth官方REST API验证令牌,实现类似next-auth的中间件路由拦截效果:

步骤1:编写中间件代码

// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';

export async function middleware(request: NextRequest) {
  // 从Cookie中获取Firebase Auth的ID令牌(键名根据你的实际登录逻辑调整)
  const idToken = request.cookies.get('__session')?.value;

  // 无令牌直接重定向登录页
  if (!idToken) {
    return NextResponse.redirect(new URL('/login', request.url));
  }

  try {
    // 调用Firebase Auth REST API验证令牌有效性
    const verifyRes = await fetch(
      `https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${process.env.NEXT_PUBLIC_FIREBASE_API_KEY}`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ idToken }),
      }
    );

    const verifyData = await verifyRes.json();

    // 令牌无效或验证失败,重定向登录页
    if (!verifyRes.ok || !verifyData.users?.length) {
      return NextResponse.redirect(new URL('/login', request.url));
    }

    // 验证通过,允许访问目标路由
    return NextResponse.next();
  } catch (err) {
    // 异常情况也重定向登录页
    return NextResponse.redirect(new URL('/login', request.url));
  }
}

// 匹配/dashboard下所有子路由
export const config = {
  matcher: '/dashboard/:path*',
};

步骤2:登录时存储令牌到Cookie

登录成功后,将Firebase返回的ID令牌存入Cookie,确保中间件能读取到:

// 登录逻辑示例(客户端/服务端组件均可)
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from '@/firebase/config';
import { cookies } from 'next/headers';

async function handleLogin(email: string, password: string) {
  const userCredential = await signInWithEmailAndPassword(auth, email, password);
  const idToken = await userCredential.user.getIdToken();

  // 设置Cookie,生产环境开启secure和httpOnly增强安全性
  cookies().set('__session', idToken, {
    httpOnly: true,
    secure: process.env.NODE_ENV === 'production',
    maxAge: 60 * 60 * 24 * 7, // 有效期7天
    path: '/',
  });
}

2. 避免React Context方案的布局闪烁

客户端Context判断认证状态时,因客户端渲染时机问题容易出现布局闪烁,可通过服务端组件前置验证结合加载状态解决:

步骤1:服务端前置验证用户状态

编写服务端工具函数获取并验证用户:

// app/lib/getUser.ts
import { cookies } from 'next/headers';

export async function getUser() {
  const idToken = cookies().get('__session')?.value;
  if (!idToken) return null;

  try {
    const verifyRes = await fetch(
      `https://identitytoolkit.googleapis.com/v1/accounts:lookup?key=${process.env.NEXT_PUBLIC_FIREBASE_API_KEY}`,
      {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ idToken }),
      }
    );

    const verifyData = await verifyRes.json();
    if (!verifyRes.ok || !verifyData.users?.length) return null;

    return verifyData.users[0];
  } catch (err) {
    return null;
  }
}

步骤2:在Dashboard布局中拦截未登录请求

将Dashboard的布局设为服务端组件,前置验证用户状态,未登录直接重定向:

// app/dashboard/layout.tsx
import { redirect } from 'next/navigation';
import { getUser } from '@/app/lib/getUser';
import { AuthProvider } from '@/app/components/AuthProvider'; // 客户端Context组件

export default async function DashboardLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const user = await getUser();

  // 未登录直接重定向,不会渲染Dashboard布局
  if (!user) {
    redirect('/login');
  }

  // 把服务端获取的用户信息传给客户端Context,保持状态同步
  return (
    <AuthProvider initialUser={{ uid: user.localId, email: user.email }}>
      <div className="dashboard-container">
        {/* Dashboard公共布局内容 */}
        {children}
      </div>
    </AuthProvider>
  );
}

步骤3:添加加载状态组件

在Dashboard路由下创建loading.tsx,服务端验证期间显示加载状态,避免空白或闪烁:

// app/dashboard/loading.tsx
export default function DashboardLoading() {
  return (
    <div className="flex justify-center items-center h-screen">
      <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
    </div>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 15:28:25