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
。以下是其用途的簡要概述:
規則類型 | |
---|---|
。讀 | 描述是否以及何時允許用戶讀取數據。 |
。寫 | 描述是否以及何時允許寫入數據。 |
.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 });
物鏡
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 });
物鏡
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" } } } }
在上面的示例中,由於第二條規則始終為false
,因此即使第一個規則始終為true
,也會拒絕對message1
節點的讀取。
下一步
您可以加深對Firebase實時數據庫安全規則的了解:
了解規則語言的下一個主要概念,即動態條件,它使您的規則可以檢查用戶授權,比較現有數據和傳入數據,驗證傳入數據,檢查來自客戶端的查詢的結構等。
查看典型的安全用例和解決這些問題的Firebase安全規則定義。