了解實時數據庫安全規則語言的核心語法

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規則在這裡無效,因為訪問不能被子路徑撤銷。

雖然它可能看起來並不直觀,但它是規則語言的強大部分,允許以最小的努力實現非常複雜的訪問權限。當我們在本指南後面介紹基於用戶的安全時,將對此進行說明。

請注意, .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
注意:此 Firebase 產品不適用於 App Clip 目標。
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
}];
迅速
注意:此 Firebase 產品不適用於 App Clip 目標。
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
注意:此 Firebase 產品不適用於 App Clip 目標。
FIRDatabaseReference *ref = [[FIRDatabase database] reference];
[[ref child:@"records/rec1"] observeSingleEventOfType:FEventTypeValue withBlock:^(FIRDataSnapshot *snapshot) {
    // SUCCESS!
}];
迅速
注意:此 Firebase 產品不適用於 App Clip 目標。
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 安全規則定義