设计安全规则结构
借助 Firestore 安全规则,您可以控制对数据库中的文档和集合的访问权限。您可以使用灵活的规则语法创建与任意情况(包括对整个数据库执行的所有写入操作和对特定文档的操作)匹配的规则。
本指南介绍安全规则的基本语法和结构。将此语法与安全规则条件结合使用可创建完整的规则集。
服务和数据库声明
Firestore 安全规则始终以以下声明开头:
service cloud.firestore {
match /databases/{database}/documents {
// ...
}
}
service cloud.firestore
声明将规则限制为在 Firestore 范围内有效,以防止 Firestore 安全规则与其他产品(例如 Cloud Storage)的规则发生冲突。
match /databases/{database}/documents
声明指定规则应该与项目中的所有 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
变量将解析为城市文档名称(例如 SF
或 NYC
)。
细化的操作
在某些情况下,将 read
和 write
细分为更细化的操作很有用。例如,您的应用可能希望对文档创建(而非文档删除)强制执行不同的条件。或者,您可能希望允许单个文档读取,但拒绝大量查询。
read
规则可以细分为 get
和 list
,而 write
规则可以细分为 create
、update
和 delete
:
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>;
}
}
}
分层数据
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() 的最大次数 |
超过任一限制都会导致权限被拒绝的错误。 某些文档访问调用可能会被缓存,缓存的调用不会计入限额。 |
嵌套 match 语句深度上限 |
10 |
在路径段中,可在一组嵌套 match 语句中使用的路径长度上限 |
100 |
可在一组嵌套 match 语句中使用的路径捕获变量数上限 |
20 |
函数调用深度上限 | 20 |
函数参数的数量上限 | 7 |
每个函数的 let 变量绑定数上限 |
10 |
递归或循环函数调用次数上限 | 0(不允许) |
每个请求中评估的表达式数量上限 | 1000 |
规则集的大小上限 | 规则集必须符合以下两种大小限制:
|