Nuxt.js项目中页面重载后如何通过Cookie Token从Firebase获取用户信息
核心问题根因
- 本地解码JWT的逻辑完全不可信:使用
JWTDecode仅对JWT做base64格式解析,未验证token签名、有效期,拿到的用户字段没有任何合法性,甚至字段匹配关系都是错的 - 关键会话信息遗漏:仅存储了1小时有效期的短期access_token,没有将长效的refreshToken同步到Cookie,页面重载后没有token自动续期的能力
- 完全绕开Firebase Auth的原生会话恢复机制,自维护的会话状态和Firebase服务端脱节,一旦上下文变化就会失效
分步修正方案
1. 调整登录环节的Cookie存储逻辑
登录拿到用户信息后,除存储短期access_token外,将长效refreshToken同步写入Cookie,设置和Firebase refreshToken匹配的过期周期(通常30天),不要仅把refreshToken存在前端内存store中——这类存储在页面重载后会被直接清空。
修正后的邮箱登录逻辑示例:
async SIGN_IN_WITH_EMAIL({commit}, form) { try { await auth.signInWithEmailAndPassword(form.email, form.password) const token = await auth.currentUser?.getIdToken(); const {email, uid, refreshToken} = auth.currentUser; commit("SET_USER", {email, uid, refreshToken}); // 短期访问token,1小时有效期 this.$storage.setCookie("access_token", token, { maxAge: 60 * 60 }) // 长期刷新token,30天有效期,用于自动续期 this.$storage.setCookie("refresh_token", refreshToken, { maxAge: 60 * 60 * 24 * 30 }) } catch (err) { console.log("error", err.message) } },
2. 修正nuxtServerInit的会话恢复逻辑
不要在本地解码JWT拿到用户信息,通过Firebase Admin的verifyIdToken方法做服务端校验,只有通过签名验证的用户信息才能提交到全局store。校验时如果遇到accessToken过期的情况,就用存储的refreshToken换取新的有效token,同步更新Cookie,实现无感续期。
修正后的服务端初始化逻辑示例:
import admin from "firebase-admin" // 复用已初始化的firebase-admin实例,避免重复初始化 const firebaseAdmin = admin.apps[0] || admin.initializeApp(/* 服务端SDK配置 */) async nuxtServerInit({commit}, {req, res}) { if (!req.headers.cookie) return; const parsed = cookieParser.parse(req.headers.cookie); const accessTokenCookie = parsed.access_token; const refreshTokenCookie = parsed.refresh_token if (!accessTokenCookie || !refreshTokenCookie) return; try { // 服务端校验token合法性,而非本地解析 const decodedToken = await firebaseAdmin.auth().verifyIdToken(accessTokenCookie) // decodedToken返回的uid是经过服务端验证的真实用户标识 commit("SET_USER", { uid: decodedToken.uid, email: decodedToken.email, refreshToken: refreshTokenCookie }); } catch (err) { // 捕获token过期/无效错误,用refreshToken换取新凭证 const newTokens = await refreshFirebaseAuthToken(refreshTokenCookie) commit("SET_USER", newTokens.userInfo) // 通过响应头给客户端更新新的accessToken Cookie res.setHeader('Set-Cookie', [ `access_token=${newTokens.accessToken}; Path=/; Max-Age=3600` ]) } }
3. 客户端侧补充会话同步兜底
页面水合完成后,挂上Firebase Auth原生的onAuthStateChanged监听,和服务端恢复的会话状态做同步,避免前后端状态不一致:
// 写入默认布局组件的mounted钩子中 mounted() { auth.onAuthStateChanged((user) => { if (user) { this.$store.commit("SET_USER", { uid: user.uid, email: user.email, refreshToken: user.refreshToken }) // 同步更新Cookie中的最新accessToken user.getIdToken().then(token => { this.$storage.setCookie("access_token", token, { maxAge: 60 * 60 }) }) } else { this.$store.commit("RESET_USER") this.$storage.removeCookie("access_token") this.$storage.removeCookie("refresh_token") } }) }
容易踩的坑
- 原有JWT解码的字段映射是错的:Firebase颁发的ID Token payload中,用户uid存储在
sub字段,不是user_id,就算解码了token,原逻辑取到的uid也是空值,无法正确赋值 - 不要把Firebase客户端的
signInWithEmailAndPassword逻辑放到服务端nuxtServerInit中执行,这类方法依赖浏览器环境,服务端的会话校验必须用Firebase Admin SDK实现 - 如果你使用静态生成模式,原代码里的
if (process.server && process.static) return;会直接跳过会话恢复逻辑,需要移除这段判断,保证静态页面重载时也能正常读取Cookie恢复用户状态
内容的提问来源于stack exchange,提问作者oktay tontaş
相关产品推荐
相关产品推荐

