Next.js SSR模式下获取当前用户数据的实现困惑与求助
我在使用Next.js的SSR(服务端渲染)模式获取用户数据时遇到了困惑。此前我一直采用React的Context API管理用户状态,该方案在常规场景下运行正常,但当需要通过SSR从API获取数据时出现了问题。我希望获取当前用户ID,以便从Firestore中获取对应的token。
以下是我当前的相关代码实现:
Auth.js 文件代码
export const AuthContext = createContext(); export function AuthProvider({ children }) { const [currentUser, setCurrentUser] = useState(null); useEffect(() => { onAuthStateChanged(auth, (user) => { setCurrentUser(user); }); }, []); return ( <AuthContext.Provider value={{ currentUser }}> {children} </AuthContext.Provider> ); }
SSR相关代码
export async function getStaticProps() { const api = { auth: "token", }; //fetch api return { props: { data, }, }; }
咱先揪出核心问题:你现在用的getStaticProps其实是**静态生成(SSG)**的方法,它在项目构建阶段就跑完了,根本拿不到当前用户的动态信息——这就是你为啥在SSR场景下拿不到用户ID的关键原因之一。
接下来一步步解决问题:
1. 换用getServerSideProps替代getStaticProps
getServerSideProps是真正的SSR入口,它会在每次用户请求时都在服务端运行,能拿到请求上下文(包括cookie、请求头这些关键信息),这才是获取用户动态数据的正确姿势。
2. 把用户认证信息存在Cookie里
你的AuthContext是纯客户端逻辑(依赖useEffect和Firebase的onAuthStateChanged),服务端根本访问不到这个状态。所以得把用户ID(或者更安全的ID Token)存在Cookie里,让服务端能读得到。
修改你的AuthProvider,加一段存Cookie的逻辑:
import Cookies from 'js-cookie'; // 可以用这个库简化Cookie操作,也可以用原生document.cookie export const AuthContext = createContext(); export function AuthProvider({ children, initialUser }) { const [currentUser, setCurrentUser] = useState(initialUser); useEffect(() => { const unsubscribe = onAuthStateChanged(auth, (user) => { setCurrentUser(user); if (user) { // 把用户ID存入Cookie,设置1天有效期,路径覆盖全站 Cookies.set('userId', user.uid, { expires: 1, path: '/' }); // 要是追求更安全,也可以存Firebase的ID Token,后面服务端验证 // user.getIdToken().then(token => Cookies.set('idToken', token, { expires: 1, path: '/' })) } else { // 用户登出时清除Cookie Cookies.remove('userId', { path: '/' }); } }); return unsubscribe; }, []); return ( <AuthContext.Provider value={{ currentUser }}> {children} </AuthContext.Provider> ); }
3. 在服务端读取Cookie并获取Firestore Token
现在getServerSideProps里就能从请求上下文里拿到Cookie,进而获取用户ID,再去Firestore拿对应的token:
// 假设你已经初始化了Firebase Admin SDK import admin from 'firebase-admin'; export async function getServerSideProps(context) { // 从请求头里解析Cookie const { cookie } = context.req.headers; let userId = null; if (cookie) { // 把Cookie字符串转成键值对 const cookieMap = cookie.split(';').reduce((acc, item) => { const [key, value] = item.trim().split('='); acc[key] = value; return acc; }, {}); userId = cookieMap.userId; } let token = null; if (userId) { // 去Firestore查询对应用户的token const userDoc = await admin.firestore().collection('users').doc(userId).get(); if (userDoc.exists) { token = userDoc.data().token; } } return { props: { token, initialUser: userId ? { uid: userId } : null } }; }
4. 把服务端数据传给组件和Context
页面组件拿到getServerSideProps返回的props后,不仅可以直接用token,还能把initialUser传给AuthProvider,避免客户端初始化时的状态空白:
export default function UserPage({ token, initialUser }) { return ( <AuthProvider initialUser={initialUser}> <div> {token ? `当前用户Token:${token}` : '请先登录'} </div> </AuthProvider> ); }
额外提醒
- 安全方面:如果存的是敏感信息,生产环境下要给Cookie加上
HttpOnly和Secure属性,防止XSS攻击。要是用Firebase的ID Token,服务端一定要调用admin.auth().verifyIdToken(idToken)验证合法性,避免伪造。 - 如果你只是需要用户的基本信息,也可以直接在服务端用ID Token解析出用户ID,不用存用户ID到Cookie,这样更安全。
内容的提问来源于stack exchange,提问作者Polle

