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

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
        }
     }
  }
}

这种安全结构允许/bar/从每当读/foo/包含子baz值为true 。该".read": false根据规则/foo/bar/这里有没有效果,因为访问不能由孩子的路径被撤销。

虽然它可能看起来并不直观,但它是规则语言的一个强大部分,允许以最少的努力实现非常复杂的访问权限。当我们在本指南后面介绍基于用户的安全时,将对此进行说明。

需要注意的是.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错误。如果我们评估我们的安全模拟器这个规则火力地堡控制台,我们可以看到,读操作被拒绝,因为没有读取的规则允许访问/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 实时数据库安全规则的理解: