如何在Next.js 13(app目录)中基于Firebase认证用户条件渲染组件?
在Next.js 13 App目录中实现服务端Firebase认证内容保护
要在服务端渲染的组件中安全地根据用户认证状态渲染内容,核心思路是在服务端验证Firebase的ID Token,避免客户端逻辑带来的内容泄露问题。以下是具体实现步骤:
1. 初始化Firebase Admin SDK(服务端)
首先需要在服务端配置Firebase Admin,用于验证客户端传来的ID Token。
创建lib/firebase-admin.js文件:
import { initializeApp, getApps, cert } from 'firebase-admin/app'; import { getAuth } from 'firebase-admin/auth'; // 从环境变量读取服务账号配置(需提前将Firebase Admin服务账号JSON转成字符串存入环境变量) const serviceAccount = JSON.parse(process.env.FIREBASE_ADMIN_SERVICE_ACCOUNT); // 避免重复初始化SDK if (!getApps().length) { initializeApp({ credential: cert(serviceAccount), }); } export const auth = getAuth();
2. 在服务端组件中验证用户身份
Next.js 13 App目录下的page.jsx默认是服务端组件,可以直接编写异步逻辑获取并验证认证状态:
修改app/page.jsx:
import { auth } from '@/lib/firebase-admin'; import { cookies } from 'next/headers'; export default async function Home() { const cookieStore = cookies(); // 从Cookie中获取客户端存储的Firebase ID Token const idToken = cookieStore.get('firebase-id-token')?.value; let isAuthenticated = false; if (idToken) { try { // 服务端验证Token有效性(自动检查过期、签名等) await auth.verifyIdToken(idToken); isAuthenticated = true; } catch (err) { // Token无效或过期,标记为未认证 isAuthenticated = false; } } return ( <main> <h1>Hello World</h1> {/* 仅认证用户可见的内容 */} {isAuthenticated && ( <p>This text should only be visible to authenticated users</p> )} </main> ); }
3. 客户端登录后同步Token到Cookie
客户端登录成功后,需要将Firebase的ID Token存入HttpOnly Cookie(防止XSS攻击),因为客户端无法直接设置HttpOnly Cookie,所以需要通过API路由处理:
客户端登录组件示例
import { getAuth, signInWithEmailAndPassword } from 'firebase/auth'; import { useRouter } from 'next/navigation'; export default function Login() { const router = useRouter(); const auth = getAuth(); const handleLogin = async (e) => { e.preventDefault(); const email = e.target.email.value; const password = e.target.password.value; try { const userCredential = await signInWithEmailAndPassword(auth, email, password); // 获取当前用户的ID Token const idToken = await userCredential.user.getIdToken(); // 调用API路由将Token存入Cookie await fetch('/api/set-token', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ idToken }), }); // 刷新页面让服务端重新获取认证状态 router.refresh(); } catch (err) { console.error('登录失败:', err.message); } }; return ( <form onSubmit={handleLogin}> <input type="email" name="email" placeholder="邮箱" required /> <input type="password" name="password" placeholder="密码" required /> <button type="submit">登录</button> </form> ); }
创建API路由存储Cookie
创建app/api/set-token/route.js:
import { NextResponse } from 'next/server'; export async function POST(request) { const { idToken } = await request.json(); // Token有效期默认1小时,和Firebase ID Token保持一致 const expiresIn = 60 * 60; return NextResponse.json( { success: true }, { headers: { 'Set-Cookie': `firebase-id-token=${idToken}; Path=/; HttpOnly; Max-Age=${expiresIn}; ${process.env.NODE_ENV === 'production' ? 'Secure;' : ''} SameSite=Lax`, }, } ); }
4. 登出逻辑(可选)
创建app/api/logout/route.js用于清除认证Cookie:
import { NextResponse } from 'next/server'; export async function POST() { return NextResponse.json( { success: true }, { headers: { 'Set-Cookie': 'firebase-id-token=; Path=/; HttpOnly; Max-Age=0; SameSite=Lax', }, } ); }
客户端登出组件示例
import { getAuth, signOut } from 'firebase/auth'; import { useRouter } from 'next/navigation'; export default function Logout() { const router = useRouter(); const auth = getAuth(); const handleLogout = async () => { await signOut(auth); await fetch('/api/logout', { method: 'POST' }); router.refresh(); }; return <button onClick={handleLogout}>登出</button>; }
注意事项
- 确保Firebase Admin服务账号的环境变量在生产环境中安全存储(不要提交到代码仓库)。
- 生产环境下务必开启Cookie的
Secure属性,确保仅在HTTPS传输。 - 服务端验证Token时,Firebase Admin会自动处理签名验证、过期检查等,无需额外逻辑。
内容的提问来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

