关于独立Firestore认证实例、多站点实例分离及邮箱密码认证实现的技术问询
使用独立Firebase实例实现多站点共享认证与数据隔离
当然可以实现这种架构!我们可以用一个专门的Firebase项目负责所有网站的登录认证,每个网站再对应一个独立的Firebase项目(带自己的Firestore存储数据),同时通过安全规则确保认证用户只能访问对应站点的资源。下面是具体的实现方法:
一、先理清楚核心架构
- 我们需要两类Firebase项目:
- 认证专用项目(Auth Project):统一处理所有网站的邮箱密码登录,存储用户的核心认证信息。
- 数据项目(Data Project):每个网站对应一个独立项目,拥有专属的Firestore实例,存储该网站的业务数据。
- 关键逻辑:让每个Data Project的安全规则信任Auth Project签发的用户令牌,这样用户在Auth Project登录后,就能合法访问对应Data Project的资源。
二、邮箱密码认证的服务端实现
因为要跨项目共享认证信息,不能直接让客户端调用Auth Project的SDK(会暴露敏感密钥),所以必须通过自己的后端中转处理:
1. 初始化Auth Project的服务端SDK
先在Auth Project的控制台下载服务端密钥(JSON格式),在你的后端(比如Node.js)中初始化Firebase Admin SDK:
const admin = require('firebase-admin'); // 替换成你的Auth Project服务端密钥文件路径 const authServiceAccount = require('./auth-project-service-account.json'); // 给这个实例命名为"auth",方便后续区分多个Firebase项目 admin.initializeApp({ credential: admin.credential.cert(authServiceAccount), databaseURL: 'https://你的auth-project-id.firebaseio.com' }, 'auth');
2. 编写登录接口处理密码验证
后端提供一个登录接口,接收客户端传来的邮箱、密码和目标Data Project的ID,完成两件事:验证密码合法性,生成能被Data Project信任的自定义令牌:
// Express.js示例的登录接口 app.post('/auth/login', async (req, res) => { const { email, password, targetProjectId } = req.body; try { const authApp = admin.app('auth'); const auth = authApp.auth(); // 用Firebase Auth的REST API验证密码(Admin SDK无直接校验密码的方法) const authResponse = await fetch(`https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key=${你的Auth Project API Key}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email, password, returnSecureToken: true }) }); const authData = await authResponse.json(); if (authData.error) { return res.status(401).json({ error: authData.error.message }); } // 通过邮箱获取用户UID,生成包含目标项目ID的自定义令牌 const user = await auth.getUserByEmail(email); const customToken = await auth.createCustomToken(user.uid, { targetProjectId }); res.json({ customToken }); } catch (error) { res.status(500).json({ error: error.message }); } });
小提示:用REST API验证密码是因为Firebase Admin SDK不支持直接校验用户密码,这样既能利用官方的密码验证逻辑,又不用自己存储密码。
3. 客户端用自定义令牌登录到Data Project
客户端拿到后端返回的自定义令牌后,用对应Data Project的Firebase SDK完成登录,之后就能正常访问该项目的Firestore了:
// Web客户端代码示例 import { initializeApp } from "firebase/app"; import { getAuth, signInWithCustomToken } from "firebase/auth"; // 初始化目标Data Project的Firebase App const dataAppConfig = { apiKey: "你的Data Project API Key", authDomain: "你的data-project-id.firebaseapp.com", projectId: "你的data-project-id", // 其他必要配置... }; const dataApp = initializeApp(dataAppConfig); const auth = getAuth(dataApp); // 调用后端登录接口 fetch('/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'user@example.com', password: 'your-password', targetProjectId: '你的data-project-id' }) }) .then(res => res.json()) .then(data => signInWithCustomToken(auth, data.customToken)) .then(userCredential => { console.log('成功登录到目标数据项目:', userCredential.user.uid); // 现在可以正常访问该项目的Firestore了 });
三、配置Data Project的Firestore安全规则
最后一步是确保只有合法的认证用户才能访问数据,规则里要验证令牌来自Auth Project,且目标项目ID匹配:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { // 自定义函数:验证用户是否有权访问当前项目 function isAuthorized() { return request.auth != null && // 确认是自定义令牌登录(来自我们的Auth Project) request.auth.token.firebase.sign_in_provider == 'custom' && // 验证令牌中的目标项目ID和当前项目ID一致 request.auth.token.targetProjectId == projectId; } // 所有文档都只允许授权用户读写 match /{document=**} { allow read, write: if isAuthorized(); } } }
四、一些额外的注意事项
- 密钥保密:Auth Project的服务端密钥和API密钥绝对不能暴露给客户端,一定要只在后端使用。
- 用户权限管理:如果需要控制用户能访问哪些Data Project,可以在Auth Project的Firestore里建一个
users集合,存储每个用户UID对应的项目权限列表,登录时再校验这个权限。 - 扩展到Storage:如果你的网站还需要用Firebase Storage,同样可以把上面的规则逻辑迁移到Storage的安全规则里,实现一致的权限控制。
内容的提问来源于stack exchange,提问作者Amsakanna
相关产品推荐
相关产品推荐

