You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firebase静态网站Google Sign-In服务端认证配置问题求助

Firebase托管静态站认证拦截失效问题排查与修复

问题根源

当前方案存在三个核心问题:

  1. 云函数仅返回授权状态,未转发请求到实际静态资源,导致认证通过后用户也无法访问网站内容
  2. 前端无登录逻辑,访客不会主动携带Authorization头发起请求,云函数的token校验形同虚设
  3. 重写规则拦截所有请求,若未排除登录页面,会导致未认证用户无法进入登录流程,形成死循环

修复方案

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 13:50:14