Firebase静态网站Google Sign-In服务端认证配置问题求助
Firebase托管静态站认证拦截失效问题排查与修复
问题根源
当前方案存在三个核心问题:
- 云函数仅返回授权状态,未转发请求到实际静态资源,导致认证通过后用户也无法访问网站内容
- 前端无登录逻辑,访客不会主动携带
Authorization头发起请求,云函数的token校验形同虚设 - 重写规则拦截所有请求,若未排除登录页面,会导致未认证用户无法进入登录流程,形成死循环
修复方案
1. 修正云函数逻辑(functions/index.js)
修改云函数,在认证通过后转发请求到Firebase托管的静态资源,未认证时重定向到登录页:
const functions = require('firebase-functions'); const admin = require('firebase-admin'); const fetch = require('node-fetch'); admin.initializeApp(); // 替换为你的Firebase托管域名 const HOSTING_DOMAIN = 'your-project-id.web.app'; exports.authenticateUser = functions.https.onRequest(async (req, res) => { // 提取标准Bearer格式的token const idToken = req.headers.authorization?.split('Bearer ')[1]; if (!idToken) { // 未认证,重定向到登录页面 return res.redirect('/login.html'); } try { const decodedToken = await admin.auth().verifyIdToken(idToken); const email = decodedToken.email; const authorizedEmails = ['user1@example.com', 'user2@example.com']; if (!authorizedEmails.includes(email)) { return res.status(403).send('无访问权限'); } // 认证通过,转发请求到托管静态资源 const url = `https://${HOSTING_DOMAIN}${req.originalUrl}`; const hostingResponse = await fetch(url, { headers: { ...req.headers, host: HOSTING_DOMAIN } }); // 将托管的响应原封不动返回给客户端 res.status(hostingResponse.status); for (const [key, value] of hostingResponse.headers.entries()) { res.setHeader(key, value); } hostingResponse.body.pipe(res); } catch (error) { // Token无效或验证失败,重定向到登录页 res.redirect('/login.html'); } });
执行命令安装依赖:在
functions目录下运行npm install node-fetch
2. 添加前端登录页面(public/login.html)
创建登录页面,集成Google Sign-In,登录后获取idToken并存储,再跳转到主页:
<!DOCTYPE html> <html> <head> <title>登录</title> <script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-app-compat.js"></script> <script src="https://www.gstatic.com/firebasejs/9.23.0/firebase-auth-compat.js"></script> </head> <body> <button id="googleLoginBtn">使用Google账号登录</button> <script> // 替换为你的Firebase项目配置 const firebaseConfig = { apiKey: "YOUR_API_KEY", authDomain: "YOUR_AUTH_DOMAIN", projectId: "YOUR_PROJECT_ID", storageBucket: "YOUR_STORAGE_BUCKET", messagingSenderId: "YOUR_SENDER_ID", appId: "YOUR_APP_ID" }; firebase.initializeApp(firebaseConfig); const auth = firebase.auth(); document.getElementById('googleLoginBtn').addEventListener('click', async () => { const provider = new firebase.auth.GoogleAuthProvider(); try { const result = await auth.signInWithPopup(provider); const user = result.user; const idToken = await user.getIdToken(); // 存储token到本地,后续请求携带 localStorage.setItem('idToken', idToken); // 跳转到主页 window.location.href = '/'; } catch (error) { alert('登录失败:' + error.message); } }); </script> </body> </html>
3. 全局请求拦截(public/js/auth.js)
在所有静态页面中引入该脚本,确保每个请求都携带Authorization头:
document.addEventListener('DOMContentLoaded', async () => { const idToken = localStorage.getItem('idToken'); if (!idToken) { window.location.href = '/login.html'; return; } // 验证token有效性,防止过期 try { const user = await firebase.auth().currentUser; if (!user) { localStorage.removeItem('idToken'); window.location.href = '/login.html'; } } catch (error) { localStorage.removeItem('idToken'); window.location.href = '/login.html'; } // 拦截fetch请求,添加Authorization头 const originalFetch = window.fetch; window.fetch = async (url, options = {}) => { options.headers = { ...options.headers, Authorization: `Bearer ${idToken}` }; return originalFetch(url, options); }; });
4. 修正firebase.json配置
排除登录页面不被重写,避免死循环:
{ "hosting": { "public": "public", "rewrites": [ { "source": "!/login.html", // 排除登录页面 "function": "authenticateUser" } ] } }
5. 重新部署资源
# 部署云函数 firebase deploy --only functions # 部署托管静态资源 firebase deploy --only hosting
关键注意事项
- 确保Firebase控制台已启用Google Sign-In,并添加了正确的授权域名
- 云函数中的
HOSTING_DOMAIN必须与你的Firebase托管实际域名一致 - 可根据需求调整客户端Firebase SDK版本,确保代码兼容
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

