Firestore 安全规则未按预期工作

Posted

技术标签:

【中文标题】Firestore 安全规则未按预期工作【英文标题】:Firestore Security Rules not working as expected 【发布时间】:2020-10-30 03:07:58 【问题描述】:

我重新安排了 Cloud Firestore 的安全规则,以防止在集合中重新创建文档。这是我使用的规则:

match /UserData/uid/DAILY_USAGES/day 
  allow create: if !exists(/databases/$(database)/documents/UserData/$(request.auth.uid)/DAILY_USAGES/$(day));
  allow read, update: if request.auth.uid == uid;

我在控制台中使用这些文档路径和用户凭据模拟了这个规则

/UserData/UoeJtUhPpJTi78HouKLIqhcRpfs48/DAILY_USAGES/09-07-2020

如果我创建一个 id 为 09-07-2020 的文档并使用上述路径进行模拟,它会失败。当我删除文档 09-07-2020 然后上面的模拟它工作。所以它在规则模拟器中运行良好。

但是当我在应用程序中尝试时,每次我尝试它都会再次创建文档。所以如果之前的文档内容不同,它会重置为我用来创建文档的默认值。

这是我在 android studio 中只使用一次创建此文档的代码

DailyUsage usage = new DailyUsage();
        usage.setUsage(0);
        usage.setTime(formatDate(date));
        FirebaseFirestore.getInstance().collection(Constants.USER_DATA_ROOT).document(uid)
                .collection(Constants.FIRESTORE_CHILD_DAILY_USAGES).document(today).set(usage);

我不想检查文档之前是否存在,因为它会减慢用户界面并且无法按预期工作。所以我尝试使用安全规则来防止重新创建文档但不处理应用程序。有人可以帮忙吗?

【问题讨论】:

我不确定您要在这里做什么。我认为您误解了allow create 的作用。该规则仅在实际创建文档时触发。当文档已经存在并且正在更新时,它不会触发。如果文档已经存在,set() 将在没有安全规则的情况下失败。 set()是用来更新的,不是创建文档吗?​​ 我不明白这个问题。 set() 用于创建文档。如果您还传递一个参数告诉它将内容合并到现有文档中,它只会更新现有文档。 我认为您应该将注意力集中在update 规则上,以便它拒绝不符合您要求的更新。听起来您可能想检查某些字段是否与其原始值不同。现在,只要用户经过身份验证,您的规则就允许使用任何字段值进行所有更新。 如果您有解决方案,请随时回答您自己的问题。 【参考方案1】:

set()method根据文档是否存在有两种行为:

如果文档不存在,则会创建它。如果文档确实存在,则其内容将被新提供的数据覆盖,除非您指定数据应合并到现有文档中

这意味着当文档已经存在时,将触发的规则将是 update 规则,因此您必须更改您的规则:

allow read, update: if request.auth.uid == uid;

在您调用 set 并且文档存在时包含必要的逻辑。

您可能想看看这个answer,关于set 和update 的使用。提到了 create method 可能适合您的需求,但不是 Web API 的一部分,不知道您正在使用的 API 上是否可用。

【讨论】:

非常感谢 Emmanuel 我了解情况。我在想 set() 只适用于 create,但知道这很清楚。如果有文档,它适用于更新。这是重要的一点。我更改了规则,因此如果 set() 数据包含 0 我将阻止。因此,我可以使用除 0 以外的任何数字更新该字段。这解决了我的问题。【参考方案2】:

感谢@DougStevenson 和@Emmanuel,我清楚地了解了情况。我在想set() 总是触发allow create 安全规则,因为我们使用set() 创建文档并使用update() 更新文档。然而这并不完全正确,set() 触发 allow update 规则,如果之前有一个具有相同 id 的文档。 set() 也用于更新合并选项,我不知道。

我的问题是关于只创建一次 Firestore 文档并防止重新创建它。然而,一方面我也在某些情况下更新了这个文档。我想防止使用默认值 0 覆盖文档。所以我更新了规则,并且知道如果值为 0,我将阻止更新。

match /UserData/uid/DAILY_USAGES/day 
      allow create: if !exists(/databases/$(database)/documents/UserData/$(request.auth.uid)/DAILY_USAGES/$(day));
                allow read: if request.auth.uid == uid; 
        allow update: if request.auth.uid == uid && request.resource.data.usage != 0;
    

在应用程序中,我没有更改任何内容并像这样使用:

DailyUsage usage = new DailyUsage();
        usage.setUsage(0);
        usage.setTime(formatDate(date));
        FirebaseFirestore.getInstance().collection(Constants.USER_DATA_ROOT).document(uid)
                .collection(Constants.FIRESTORE_CHILD_DAILY_USAGES).document(today).set(usage);

此代码在应用启动时触发,如果没有具有相同 id 的文档allow create 安全规则触发并创建文档。但是,如果有一个文档allow update 安全规则触发并检查即将到来的资源数据,如果使用值为 0,则阻止更新。在应用程序的代码块中,它在应用程序启动时将值发送为 0,因此不允许更新任何内容在文档中。

【讨论】:

以上是关于Firestore 安全规则未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

jQuery:验证规则未按预期工作

Apache Rewrite 规则未按预期工作

Firebase云功能Firestore触发onWrite在本地测试时未按预期运行

Firestore创建的安全规则不符合预期

Azure NSG 未按预期工作

在 Firestore 安全规则“列表”操作中使用变量