Cloud Firestore 安全規則的編寫條件

本指南以建立安全規則指南為基礎,展示如何為 Cloud Firestore 安全規則新增條件。如果您不熟悉 Cloud Firestore 安全性規則的基礎知識,請參閱入門指南。

Cloud Firestore 安全性規則的主要建置區塊是條件。條件是一個布林表達式,用於確定是否應允許或拒絕特定操作。使用安全規則編寫檢查使用者身份驗證、驗證傳入資料甚至存取資料庫其他部分的條件。

驗證

最常見的安全規則模式之一是根據使用者的身份驗證狀態控制存取。例如,您的應用程式可能希望僅允許登入使用者寫入資料:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to access documents in the "cities" collection
    // only if they are authenticated.
    match /cities/{city} {
      allow read, write: if request.auth != null;
    }
  }
}

另一種常見模式是確保使用者只能讀取和寫入自己的資料:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the user
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /users/{userId} {
      allow read, update, delete: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null;
    }
  }
}

如果您的應用程式使用 Firebase Authentication 或Google Cloud Identity Platform ,則request.auth變數包含要求資料的用戶端的驗證資訊。有關request.auth的更多信息,請參閱參考文件

數據驗證

許多應用程式將存取控制資訊儲存為資料庫中文件的欄位。 Cloud Firestore 安全性規則可以根據文件資料動態允許或拒絕存取:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

resource變數引用所請求的文檔, resource.data是儲存在文檔中的所有欄位和值的對應。有關resource變數的更多信息,請參閱參考文件

寫入資料時,您可能會想要將傳入資料與現有資料進行比較。在這種情況下,如果您的規則集允許掛起寫入,則request.resource變數包含文件的未來狀態。對於僅修改文件欄位子集的update操作, request.resource變數將包含作業後待處理的文件狀態。您可以檢查request.resource中的欄位值以防止不必要或不一致的資料更新:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure all cities have a positive population and
    // the name is not changed
    match /cities/{city} {
      allow update: if request.resource.data.population > 0
                    && request.resource.data.name == resource.data.name;
    }
  }
}

存取其他文檔

使用get()exists()函數,您的安全規則可以根據資料庫中的其他文件評估傳入請求。 get()exists()函數都需要完全指定的文檔路徑。當使用變數為get()exists()建構路徑時,需要使用$(variable)語法明確轉義變數。

在下面的範例中, database變數由 match 語句match /databases/{database}/documents捕獲並用於形成路徑:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before
      // allowing any writes to the 'cities' collection
      allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid))

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
    }
  }
}

對於寫入,您可以使用getAfter()函數在交易或批次寫入完成之後但在交易或批次提交之前存取文件的狀態。與get()一樣, getAfter()函數採用完全指定的文檔路徑。您可以使用getAfter()定義必須作為事務或批次一起發生的寫入集。

存取通話限制

每個規則集評估的文件存取呼叫有限制:

  • 10 用於單一文檔請求和查詢請求。
  • 20 用於多文檔讀取、事務和批次寫入。之前的 10 個限制也適用於每個操作。

    例如,假設您建立一個包含 3 個寫入作業的批次寫入要求,而您的安全性規則使用 2 個文件存取呼叫來驗證每個寫入。在這種情況下,每個寫入使用其 10 個存取呼叫中的 2 個,而批次寫入請求使用其 20 個存取呼叫中的 6 個。

超過任一限制都會導致權限被拒絕錯誤。某些文件存取呼叫可能會被緩存,且快取的呼叫不計入限制。

有關這些限制如何影響交易和批量寫入的詳細說明,請參閱保護原子操作指南。

訪問通話和定價

使用這些函數會在資料庫中執行讀取操作,這表示即使您的規則拒絕該要求,您也需要為讀取文件付費。請參閱Cloud Firestore 定價以了解更多具體的計費資訊。

自訂功能

隨著安全規則變得越來越複雜,您可能會想要將條件集包裝在可以在規則集中重複使用的函數中。安全規則支援自訂功能。自訂函數的語法有點像 JavaScript,但安全規則函數是用特定於域的語言編寫的,具有一些重要的限制:

  • 函數只能包含單一return語句。它們不能包含任何額外的邏輯。例如,它們無法執行循環或呼叫外部服務。
  • 函數可以自動從定義的範圍內存取函數和變數。例如,在service cloud.firestore範圍內定義的函數可以存取resource變數和內建函數,例如get()exists()
  • 函數可以呼叫其他函數,但不能遞歸。總呼叫堆疊深度限制為 10。
  • 在規則版本v2中,函數可以使用let關鍵字定義變數。函數最多可以有 10 個 let 綁定,但必須以 return 語句結束。

函數是使用function關鍵字定義的,並採用零個或多個參數。例如,您可能希望將上面範例中使用的兩種類型的條件組合成一個函數:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

隨著規則複雜性的增加,在安全規則中使用函數可以使它們更易於維護。

規則不是過濾器

一旦您保護資料並開始編寫查詢,請記住安全規則不是過濾器。您無法為集合中的所有文件編寫查詢並期望 Cloud Firestore 僅傳回目前用戶端有權存取的文件。

例如,採用以下安全規則:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

拒絕:此規則拒絕以下查詢,因為結果集可能包含visibilitypublic文件:

網路
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

允許:此規則允許以下查詢,因為where("visibility", "==", "public")子句保證結果集符合規則的條件:

網路
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

Cloud Firestore 安全性規則會根據每個查詢的潛在結果評估每個查詢,如果它可能傳回用戶端無權讀取的文檔,則該請求將會失敗。查詢必須遵循安全規則設定的約束。有關安全規則和查詢的更多信息,請參閱安全查詢資料

下一步