构建 Cloud Firestore 安全规则

借助 Cloud Firestore 安全规则,您可以控制对数据库中的文档和集合的访问权限。您可以使用灵活的规则语法创建与任意情况(包括对整个数据库执行的所有写入操作和对特定文档的操作)匹配的规则。

本指南介绍安全规则的基本语法和结构。将此语法与安全规则条件结合使用可创建完整的规则集。

服务和数据库声明

Cloud Firestore 安全规则始终以以下声明开头:

service cloud.firestore {
  match /databases/{database}/documents {
    // ...
  }
}

service cloud.firestore 声明将规则限制为在 Cloud Firestore 范围内有效,以防止 Cloud Firestore 安全规则与其他产品(例如 Cloud Storage)的规则发生冲突。

match /databases/{database}/documents 声明指定规则应该与项目中的所有 Cloud Firestore 数据库都匹配。目前,每个项目只有一个数据库,名为 (default)

基本的读取/写入规则

基本规则由一个 match 语句(指定文档路径)和一个 allow 表达式(详细说明何时允许读取指定的数据)构成:

service cloud.firestore {
  match /databases/{database}/documents {

    // Match any document in the 'cities' collection
    match /cities/{city} {
      allow read: if <condition>;
      allow write: if <condition>;
    }
  }
}

所有 match 语句都应指向文档,而不是集合。Match 语句可以指向特定的文档(如 match /cities/SF),也可以使用通配符指向指定路径下的任意文档(如 match /cities/{city})。

在上面的示例中,match 语句使用了 {city} 通配符语法。这意味着相应规则适用于 cities 集合中的任何文档(例如 /cities/SF/cities/NYC)。在对 match 语句中的 allow 表达式求值时,city 变量将解析为城市文档名称(例如 SFNYC)。

细化的操作

在某些情况下,将 readwrite 细分为更细化的操作很有用。例如,您的应用可能希望对文档创建(而非文档删除)强制执行不同的条件。或者,您可能希望允许单个文档读取,但拒绝大量查询。

read 规则可以细分为 getlist,而 write 规则可以细分为 createupdatedelete

service cloud.firestore {
  match /databases/{database}/documents {
    // A read rule can be divided into get and list rules
    match /cities/{city} {
      // Applies to single document read requests
      allow get: if <condition>;

      // Applies to queries and collection read requests
      allow list: if <condition>;
    }

    // A write rule can be divided into create, update, and delete rules
    match /cities/{city} {
      // Applies to writes to nonexistent documents
      allow create: if <condition>;

      // Applies to writes to existing documents
      allow update: if <condition>;

      // Applies to delete operations
      allow delete: if <condition>;
    }
  }
}

分层数据

Cloud Firestore 中的数据以文档集合的形式出现,每个文档可以通过子集合来扩展这种层次结构。了解安全规则如何与分层数据进行交互是非常重要的。

假设 cities 集合中的每个文档都包含一个 landmarks 子集合。由于安全规则仅适用于匹配的路径,因此在 cities 集合上定义的访问权限控制规则不适用于 landmarks 子集合。不过,您可以编写明确的规则来控制对子集合的访问:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      allow read, write: if <condition>;

        // Explicitly define rules for the 'landmarks' subcollection
        match /landmarks/{landmark} {
          allow read, write: if <condition>;
        }
    }
  }
}

嵌套 match 语句时,内层 match 语句的路径始终是相对于外层 match 语句的路径而言的。因此,以下规则集是等效的:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      match /landmarks/{landmark} {
        allow read, write: if <condition>;
      }
    }
  }
}
service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city}/landmarks/{landmark} {
      allow read, write: if <condition>;
    }
  }
}

递归通配符

如果您要将规则应用于任意深度的层次结构,请使用递归通配符语法 {name=**}:例如:

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

使用递归通配符语法时,通配符变量将包含整个匹配的路径段,即使相应文档位于多层嵌套的子集合中也是如此。例如,上面列出的规则将与位于 /cities/SF/landmarks/coit_tower 中的文档匹配,document 变量的值将是 SF/landmarks/coit_tower

但请注意,递归通配符的行为取决于规则版本。

版本 1

安全规则默认使用版本 1。在版本 1 中,递归通配符与一个或多个路径项匹配。它们不能与空路径匹配,因此 match /cities/{city}/{document=**} 将与子集合(而非 cities 集合)中的文档匹配,而 match /cities/{document=**} 将同时与 cities 集合和子集合中的文档匹配。

递归通配符必须位于匹配语句的末尾。

版本 2

在安全规则的版本 2 中,递归通配符与零个或更多路径项匹配。match/cities/{city}/{document=**} 将与任何子集合中的文档以及 cities 集合中的文档匹配。

您必须将 rules_version = '2'; 添加到安全规则的第一行来选择启用版本 2:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the cities collection as well as any document
    // in a subcollection.
    match /cities/{city}/{document=**} {
      allow read, write: if <condition>;
    }
  }
}

您最多只能在每个匹配语句中使用一个递归通配符,但在版本 2 中,可以将此通配符放在匹配语句的任何位置。例如:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the songs collection group
    match /{path=**}/songs/{song} {
      allow read, write: if <condition>;
    }
  }
}

如果您使用集合组查询,必须使用版本 2。请参阅安全地进行集合组查询

重叠的匹配语句

某个文档可以与多个 match 语句匹配。如果有多个 allow 表达式与某个请求匹配,只要任何一个条件为 true,就允许访问:

service cloud.firestore {
  match /databases/{database}/documents {
    // Matches any document in the 'cities' collection.
    match /cities/{city} {
      allow read, write: if false;
    }

    // Matches any document in the 'cities' collection or subcollections.
    match /cities/{document=**} {
      allow read, write: if true;
    }
  }
}

在上面的示例中,因为第二个规则始终为 true,所以系统允许对 cities 集合进行所有读写操作,即使第一个规则始终为 false 也是如此。

安全规则限制

在处理安全规则时,请注意以下限制:

限制 详细信息
每个请求调用 exists()get()getAfter() 的最大次数
  • 10 - 对于单文档请求和查询请求。
  • 20 - 对于多文档读取、事务和批量写入。前面的 10 次限制也适用于每个操作。

    例如,假设您创建了一个包含 3 次写入操作的批量写入请求,并且您的安全规则使用 2 次文档访问调用来验证每次写入。在此情况下,每次写入会使用 10 次访问调用限额中的 2 次调用,而批量写入请求则会使用 20 次访问调用限额中的 6 次调用。

超过任一限制都会导致权限被拒绝的错误。

某些文档访问调用可能会被缓存,缓存的调用不会计入限额。

嵌套 match 语句深度上限 10
在路径段中,可在一组嵌套 match 语句中使用的路径长度上限 100
可在一组嵌套 match 语句中使用的路径捕获变量数上限 20
函数调用深度上限 20
函数参数的数量上限 7
每个函数的 let 变量绑定数上限 10
递归或循环函数调用次数上限 0(不允许)
每个请求中评估的表达式数量上限 1000
规则集的大小上限 规则集必须符合以下两种大小限制:
  • 对于从 Firebase 控制台或使用 firebase deploy 从 CLI 发布的规则集文本源,其大小不得超过 256 KB。
  • 对于 Firebase 处理该源并在后端上将其激活时生成的编译规则集,其大小不得超过 250 KB。

后续步骤