Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

學習實時數據庫規則語言的核心語法

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 實時數據庫安全規則的理解: