【问题标题】:Firebase - Best Practice For Server Firestore Reads For Server-Side RenderingFirebase - 服务器端渲染的服务器 Firestore 读取的最佳实践
【发布时间】:2019-06-01 15:59:37
【问题描述】:

我有一个使用 firebase firestore 的服务器端渲染 reactjs 应用程序。

我的网站有一个区域用于服务器端呈现需要从 Firestore 检索的内容。

目前,我正在使用 firestore 规则来允许任何人从这些特定文档中读取数据

让我担心的是,一些坏人可能会设置一个脚本来不断地读取我的数据库并累积我的账单(因为我们是按每次读取收费的,似乎让任何人执行读取都是不明智的。)

现行规则

// Allow anonymous users to read feeds
match /landingPageFeeds/{pageId}/feeds/newsFeed {
  allow read: if true;
}

最好的前进方式?

如何允许我的服务器端脚本从 Firestore 读取,但不允许其他人这样做?

请记住,这是一个初始操作,它先在服务器端运行,然后再将客户端与预加载状态结合起来。此功能/操作也与客户端共享以进行页面到页面导航。

我考虑过匿名登录 - 这很有效,但是,这会在每次页面加载时生成一个新的匿名用户 - 而且 Firebase 确实会限制新的电子邮件/密码和匿名用户帐户。这似乎不实用。

【问题讨论】:

  • 匿名身份验证不会在每次页面加载时创建新用户,除非您对其进行编码。在页面加载之间甚至在浏览器启动之间会记住创建的用户 - 您只需检查是否是这种情况。这是一个常见的错误。
  • 还请记住,如果您的页面加载导致从数据库读取,那么不良行为者所要做的就是重复重新加载您的页面以引起不必要的读取。你并没有真正解决问题,你只是在改变它。底线是,如果您公开公开项目的任何服务,您并不能真正阻止人们对您的项目产生费用。
  • 匿名用户账户的创建是在服务器端的初始动作上运行的,立即与匿名用户一起读取数据,保存到状态,然后将这个预加载的状态通过水合物发送给客户端的初始加载,每次我重新加载时它都会创建一个匿名用户,我很惊讶客户端 SDK 的这一部分在服务器端工作。 (我为新用户设置设置电子邮件通知)。通过这种方法(firebase.google.com/docs/auth/web/anonymous-auth)所以每次刷新都会发生这种情况。我猜因为限制是基于 IP 的,所以这不是问题吗?
  • 我打算尝试通过服务器端缓存来解决这个问题——这样点击重新加载的人只会得到缓存的响应(初始操作不会再次运行)。我更担心第三方机器人会嗅探不安全的 Firestore 数据库。
  • 安全规则不会影响来自 Admin SDK 的访问,后者用于运行服务器端代码以访问 Firebase 项目。

标签: reactjs firebase firebase-authentication google-cloud-firestore server-side-rendering


【解决方案1】:

解决方案

根据 Doug 的评论,我更多地考虑了管理 SDK。我最终在 firebase 函数中为需要可以缓存的安全 Firestore 读取的匿名请求创建了一个单独的 API。

目标

  1. 继续拒绝公开读取我的 Firestore 数据库
  2. 允许匿名用户触发对需要来自 Firestore 数据库数据的服务器端呈现的 reactjs 页面(如首次访问者、搜索引擎)的 firestore 读取。
  3. 通过对响应使用服务器端 CDN 缓存来防止“读取垃圾邮件”第三方可能以数百万次读取访问我的数据库以增加我的云成本。 (通过在循环中调用不必要的读取,我曾经意外地支付了巨额账单 - 我想确保陌生人不会恶意这样做)

Admin SDK 和 Firebase 函数缓存

管理 SDK 允许我安全地从 firestore 读取数据。我的firestore security rules 可以拒绝未经身份验证的用户访问。

处理 GET 请求的 Firebase 函数支持 server caching the response。这意味着来自相同查询的后续命中不会重新运行我的所有函数(firebase 读取、其他函数调用) - 它只会立即再次以相同的数据响应。

流程

  1. 匿名客户端访问服务器端渲染的 reactjs 页面
  2. 服务器上的初始加载渲染会触发一个 firebase 函数(https 触发器)
  3. Firebase 函数使用 Admin SDK 从安全的 Firestore 数据库中读取数据
  4. 函数将响应缓存 3 小时 res.set('Cache-Control', 'public, max-age=600, s-maxage=10800');
  5. 接下来 3 小时内来自任何地方的任何客户端的后续请求都将从缓存中得到处理 - 避免不必要的读取或额外的计算/资源使用

注意 - 本地缓存不起作用 - 必须部署到 Firebase 以测试缓存效果。

示例函数

const functions = require("firebase-functions");
const cors = require('cors')({origin: true});
const { sendResponse } = require("./includes/sendResponse");
const { getFirestoreDataWithAdminSDK } = require("./includes/getFirestoreDataWithAdminSDK");


const cachedApi = functions.https.onRequest((req, res) => {
    cors(req, res, async () => {
        // Set a cache for the response to limit the impact of identical request on expensive resources
        res.set('Cache-Control', 'public, max-age=600, s-maxage=10800');

        // If POST - response with bad request code - POST requests are not cached
        if(req.method === "POST") {
            return sendResponse(res, 400);
        } else {

            // Get GET request action from query
            let action = (req.query.action) ? req.query.action : null;
            console.log("Action: ", action);

            try {
                // Handle Actions Appropriately
                switch(true) {
                    
                    // Get Feed Data
                    case(action === "feed"): {
                        console.log("Getting feed...");

                        // Get feed id
                        let feedId = (req.query.feedId) ? req.query.feedId : null;

                        // Get feed data
                        let feedData = await getFirestoreDataWithAdminSDK(feedId);

                        return sendResponse(res, 200, feedData);
                    }

                    // No valid action specified
                    default: {
                        return sendResponse(res, 400);
                    }
                }
            } catch(err) {
                console.log("Cached API Error: ", err);
                return sendResponse(res, 500);
            }
        }
    });
});

module.exports = {
    cachedApi
}

【讨论】:

    猜你喜欢
    • 2010-12-21
    • 2018-03-06
    • 1970-01-01
    • 2020-08-15
    • 1970-01-01
    • 2015-07-26
    • 2019-11-13
    • 1970-01-01
    • 2020-11-11
    相关资源
    最近更新 更多