Firebase Realtime Database 安全规则允许您控制对数据库中存储的数据的访问权限。灵活的规则语法允许您创建与任意情况匹配的规则,涵盖对数据库执行的所有写入操作和对各个节点的操作等各种场景。
Realtime Database 安全规则是针对数据库的声明性配置。这意味着这些规则的定义独立于产品逻辑之外。这种方法具有许多优点:客户端不负责强制执行安全策略,即使有缺陷的实现也不会损坏您的数据,而且或许最重要的一点是,无需中间受托对象(例如服务器)来保护数据。
本主题介绍 Realtime Database 安全规则用于创建完整规则集的基本语法和结构。
构建安全规则
Realtime Database 安全规则由类似 JavaScript 的表达式(包含在 JSON 文件中)构成。规则的结构应符合您在数据库中存储的数据的结构。
基本规则可识别一组要保护的节点、所涉及的“访问方法”(例如,读取、写入)和允许或拒绝访问的“条件”。在以下示例中,我们的条件将是简单的 true
和 false
语句,但在下一个主题中,我们将介绍更多用于表达条件的动态方式。
例如,如果我们想要确保 parent_node
下 child_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
。下面简要介绍了这三种规则的用途:
规则类型 | |
---|---|
.read | 描述是否以及何时允许用户读取数据。 |
.write | 描述是否以及何时允许写入数据。 |
.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
规则,该规则确保除了 title
和 color
之外,widget
没有其他子节点。任何会导致创建其他子节点的写入均将失败。
{ "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 });
Objective-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 }];
Swift
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 })
Java
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 }); });
REST
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 });
Objective-C
FIRDatabaseReference *ref = [[FIRDatabase database] reference]; [[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) { // SUCCESS! }];
Swift
var ref = FIRDatabase.database().reference() ref.child("records/rec1").observeSingleEventOfType(.Value, withBlock: { snapshot in // SUCCESS! })
Java
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 } });
REST
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" } } } }
在上面的示例中,因为第二个规则始终为 false
,所以系统将拒绝对 message1
节点进行读取,即使第一个规则始终为 true
也是如此。
后续步骤
您可以深入了解 Firebase Realtime Database 安全规则:
了解规则语言的下一个主要概念(动态条件),这些条件可让您的规则检查用户授权、比较现有数据和传入数据、验证传入数据、检查来自客户端的查询的结构等等。
查看典型的安全使用场景以及用于解决这些场景的 Firebase 安全规则定义。