Firebase 实时数据库安全规则允许您控制对存储在数据库中的数据的访问。灵活的规则语法允许您创建匹配任何内容的规则,从对数据库的所有写入到对单个节点的操作。
实时数据库安全规则是数据库的声明性配置。这意味着规则是与产品逻辑分开定义的。这有很多优点:客户端不负责执行安全性,错误的实现不会损害您的数据,也许最重要的是,不需要中间裁判(例如服务器)来保护数据不受外界影响。
本主题介绍用于创建完整规则集的实时数据库安全规则的基本语法和结构。
构建您的安全规则
实时数据库安全规则由 JSON 文档中包含的类似 JavaScript 的表达式组成。您的规则结构应遵循您存储在数据库中的数据结构。
基本规则确定一组要保护的节点、涉及的访问方法(例如,读取、写入)以及允许或拒绝访问的条件。在以下示例中,我们的条件将是简单true
和false
语句,但在下一个主题中,我们将介绍更多动态的方式来表达条件。
因此,例如,如果我们试图在child_node
下保护parent_node
,则要遵循的一般语法是:
{ "rules": { "parent_node": { "child_node": { ".read": <condition>, ".write": <condition>, ".validate": <condition>, } } } }
让我们应用这个模式。例如,假设您正在跟踪消息列表并拥有如下所示的数据:
{ "messages": { "message0": { "content": "Hello", "timestamp": 1405704370369 }, "message1": { "content": "Goodbye", "timestamp": 1405704395231 }, ... } }
您的规则应该以类似的方式构建。下面是一组可能对该数据结构有意义的只读安全规则。此示例说明了我们如何指定应用规则的数据库节点以及在这些节点上评估规则的条件。
{ "rules": { // For requests to access the 'messages' node... "messages": { // ...and the individual wildcarded 'message' nodes beneath // (we'll cover wildcarding variables more a bit later).... "$message": { // For each message, allow a read operation if <condition>. In this // case, we specify our condition as "true", so read access is always granted. ".read": "true", // For read-only behavior, we specify that for write operations, our // condition is false. ".write": "false" } } } }
基本规则操作
根据对数据执行的操作类型,可使用三种类型的规则来实施安全性: .write
、 .read
和.validate
。以下是他们目的的简要总结:
规则类型 | |
---|---|
。读 | 描述是否以及何时允许用户读取数据。 |
。写 | 描述是否以及何时允许写入数据。 |
。证实 | 定义格式正确的值的外观、它是否具有子属性以及数据类型。 |
通配符捕获变量
所有规则语句都指向节点。语句可以指向特定节点或使用$
通配符捕获变量指向层次结构级别的节点集。使用这些捕获变量来存储节点键的值,以便在后续规则语句中使用。这种技术可以让您编写更复杂的规则条件,我们将在下一个主题中详细介绍。
{ "rules": { "rooms": { // this rule applies to any child of /rooms/, the key for each room id // is stored inside $room_id variable for reference "$room_id": { "topic": { // the room's topic can be changed if the room id has "public" in it ".write": "$room_id.contains('public')" } } } } }
动态$
变量也可以与常量路径名并行使用。在这个例子中,我们使用$other
变量来声明一个.validate
规则,以确保widget
除了title
和color
之外没有其他孩子。任何会导致创建额外子项的写入都将失败。
{ "rules": { "widget": { // a widget can have a title or color attribute "title": { ".validate": true }, "color": { ".validate": true }, // but no other child paths are allowed // in this case, $other means any key excluding "title" and "color" "$other": { ".validate": false } } } }
读写规则级联
.read
和.write
规则从上到下工作,较浅的规则覆盖较深的规则。如果一条规则在特定路径上授予读取或写入权限,那么它也会授予对其下所有子节点的访问权限。考虑以下结构:
{ "rules": { "foo": { // allows read to /foo/* ".read": "data.child('baz').val() === true", "bar": { /* ignored, since read was allowed already */ ".read": false } } } }
此安全结构允许在/foo/
包含值为true
的子baz
时读取/bar/
。 /foo/bar/
下的".read": false
规则在这里无效,因为访问不能被子路径撤销。
虽然它可能看起来并不直观,但它是规则语言的强大部分,允许以最小的努力实现非常复杂的访问权限。当我们在本指南后面介绍基于用户的安全时,将对此进行说明。
请注意, .validate
规则不会级联。为了允许写入,必须在层次结构的所有级别满足所有验证规则。
规则不是过滤器
规则以原子方式应用。这意味着如果在该位置或在授予访问权限的父位置没有规则,则读取或写入操作会立即失败。即使每个受影响的子路径都可以访问,在父位置的读取也会完全失败。考虑这个结构:
{ "rules": { "records": { "rec1": { ".read": true }, "rec2": { ".read": false } } } }
如果不理解规则是自动评估的,那么获取/records/
路径似乎会返回rec1
而不是rec2
。然而,实际结果是错误的:
JavaScript
var db = firebase.database(); db.ref("records").once("value", function(snap) { // success method is not called }, function(err) { // error callback triggered with PERMISSION_DENIED });
目标-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[_ref child:@"records"] observeSingleEventOfType:FIRDataEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // success block is not called } withCancelBlock:^(NSError * _Nonnull error) { // cancel block triggered with PERMISSION_DENIED }];
迅速
var ref = FIRDatabase.database().reference() ref.child("records").observeSingleEventOfType(.Value, withBlock: { snapshot in // success block is not called }, withCancelBlock: { error in // cancel block triggered with PERMISSION_DENIED })
爪哇
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // success method is not called } @Override public void onCancelled(FirebaseError firebaseError) { // error callback triggered with PERMISSION_DENIED }); });
休息
curl https://docs-examples.firebaseio.com/rest/records/ # response returns a PERMISSION_DENIED error
由于/records/
的读取操作是原子的,并且没有读取规则授予对/records/
下所有数据的访问权限,因此这将引发PERMISSION_DENIED
错误。如果我们在Firebase 控制台的安全模拟器中评估此规则,我们可以看到读取操作被拒绝,因为没有读取规则允许访问/records/
路径。但是,请注意, rec1
的规则从未被评估过,因为它不在我们请求的路径中。要获取rec1
,我们需要直接访问它:
JavaScript
var db = firebase.database(); db.ref("records/rec1").once("value", function(snap) { // SUCCESS! }, function(err) { // error callback is not called });
目标-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
迅速
var ref = FIRDatabase.database().reference() ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in // SUCCESS! })
爪哇
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("records/rec1"); ref.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot snapshot) { // SUCCESS! } @Override public void onCancelled(FirebaseError firebaseError) { // error callback is not called } });
休息
curl https://docs-examples.firebaseio.com/rest/records/rec1 # SUCCESS!
重叠语句
有可能不止一条规则应用于一个节点。在多个规则表达式标识一个节点的情况下,如果任何条件为false
,访问方法将被拒绝:
{ "rules": { "messages": { // A rule expression that applies to all nodes in the 'messages' node "$message": { ".read": "true", ".write": "true" }, // A second rule expression applying specifically to the 'message1` node "message1": { ".read": "false", ".write": "false" } } } }
在上面的示例中,将拒绝读取message1
节点,因为第二条规则始终为false
,即使第一条规则始终为true
。
下一步
您可以加深对 Firebase 实时数据库安全规则的理解:
学习规则语言的下一个主要概念,动态条件,它让您的规则检查用户授权、比较现有数据和传入数据、验证传入数据、检查来自客户端的查询结构等等。
查看典型的安全用例和解决这些用例的 Firebase 安全规则定义。