学习实时数据库规则语言的核心语法

Firebase 实时数据库安全规则允许您控制对存储在数据库中的数据的访问。灵活的规则语法允许您创建匹配任何内容的规则,从对数据库的所有写入到对单个节点的操作。

实时数据库安全规则是数据库的声明性配置。这意味着规则是与产品逻辑分开定义的。这有许多优点:客户端不负责实施安全性,有缺陷的实现不会危及您的数据,也许最重要的是,不需要中间裁判(例如服务器)来保护数据不受外界影响。

本主题介绍用于创建完整规则集的基本语法和结构实时数据库安全规则。

构建您的安全规则

实时数据库安全规则由包含在 JSON 文档中的类似 JavaScript 的表达式组成。您的规则结构应遵循您存储在数据库中的数据结构。

基本规则确定一组要保护的节点、所涉及的访问方法(例如,读、写)以及允许或拒绝访问的条件。在以下示例中,我们的条件将是简单的truefalse语句,但在下一个主题中,我们将介绍表达条件的更多动态方法。

因此,例如,如果我们试图保护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除了titlecolor之外没有其他子项。任何会导致创建额外子项的写入都将失败。

{
  "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规则在这里不起作用,因为访问不能".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被评估,因为它不在我们请求的路径中。要获取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 安全规则定义