Next.js 13新app目录下SSR认证:如何传递用户对象至子组件
解决方案与改进建议
核心思路:服务器组件Props传递(无需use client)
在Next.js App Router中,布局默认是服务器组件,可以直接异步获取用户数据,再通过Props层层传递给所有子组件,完全避开客户端状态管理(如Context),实现纯SSR认证,页面加载时直接渲染用户信息。
1. 布局中获取用户并传递给子组件
在根布局(或共享布局)中调用你的服务器函数获取用户,通过React.cloneElement将用户数据注入所有子页面/组件的Props:
// app/layout.tsx - 默认服务器组件,无需"use client" import { cookies } from 'next/headers' import { redirect } from 'next/navigation' import { getUserDetails } from '@/lib/server-utils' // 你的用户获取函数 export default async function RootLayout({ children, }: { children: React.ReactNode }) { // 从HttpOnly Cookie获取认证Token(推荐用Cookie而非请求头,更安全) const authToken = cookies().get('auth-token')?.value let user = null if (authToken) { try { user = await getUserDetails(authToken) } catch (err) { // Token无效时清除Cookie并重定向登录页 cookies().delete('auth-token') redirect('/login') } } // 未登录且非登录页时重定向 const pathname = request.nextUrl.pathname if (!user && pathname !== '/login') { redirect('/login') } // 将用户数据注入所有子元素的Props const childrenWithUser = React.Children.map(children, child => { if (React.isValidElement(child)) { return React.cloneElement(child, { user }) } return child }) return ( <html lang="zh-CN"> <body>{childrenWithUser}</body> </html> ) }
2. 子组件/页面接收用户数据
所有子页面、组件(服务器组件)可以直接通过Props接收用户数据,无需任何客户端指令:
// app/dashboard/page.tsx - 服务器组件 export default function DashboardPage({ user }) { return ( <div> <h1>欢迎回来,{user.nickname}</h1> <UserProfile user={user} /> </div> ) } // 子组件(服务器组件) function UserProfile({ user }) { return <p>绑定邮箱:{user.email}</p> }
改进方案:用Server Action管理Cookie
你之前修改Cookie失败是因为仅能在Server Action/Route Handler中操作Cookie,登录时用Server Action设置HttpOnly Cookie,安全性更高:
// app/actions/login.ts 'use server' import { cookies } from 'next/headers' import { authenticateUser } from '@/lib/server-utils' // 你的登录验证函数 export async function login(credentials: { username: string; password: string }) { const user = await authenticateUser(credentials) if (!user) return { success: false, error: '账号或密码错误' } // 设置HttpOnly、Secure的认证Cookie cookies().set('auth-token', user.token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', maxAge: 60 * 60 * 24 * 7, // 有效期7天 path: '/', }) return { success: true } }
性能优化:服务器端缓存用户数据
为避免每次请求都调用数据库,可在服务器端用Redis等工具缓存用户信息,以Token为Key缓存5-10分钟:
// @/lib/server-utils.ts import Redis from 'ioredis' const redis = new Redis(process.env.REDIS_URL) export async function getUserDetails(token: string) { // 先查缓存 const cachedUser = await redis.get(`user:${token}`) if (cachedUser) return JSON.parse(cachedUser) // 缓存未命中则查数据库 const user = await db.user.findUnique({ where: { token } }) if (!user) throw new Error('无效Token') // 写入缓存 await redis.set(`user:${token}`, JSON.stringify(user), 'EX', 300) // 5分钟过期 return user }
优势总结
- 纯SSR实现,页面加载时直接渲染用户信息,无认证加载状态
- 无需
use client,完全避开客户端状态管理的复杂度 - 同一布局下的页面切换不会重复执行用户获取逻辑,提升导航效率
- HttpOnly Cookie避免XSS攻击,安全性更高
内容的提问来源于stack exchange,提问作者Idan Masas
相关产品推荐
相关产品推荐

