編寫安全性規則的條件

本指南以建立安全性規則指南為基礎,說明如何為 Firestore 安全性規則新增條件。如果您不熟悉 Firestore 安全性規則的基本概念,請參閱入門指南。

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 Platformrequest.auth 變數會包含要求資料的用戶端驗證資訊。如要進一步瞭解 request.auth,請參閱參考說明文件

資料驗證

許多應用程式會將存取權控管資訊儲存為資料庫中文件上的欄位。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 /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 項文件存取呼叫來驗證各項寫入作業。此時,各項寫入作業會使用 2 項存取呼叫 (上限為 10 項),批次寫入要求則會使用 6 項存取呼叫 (上限為 20 項)。

超過任一項限制都會引發權限遭拒的錯誤。系統可能會快取部分的文件存取呼叫,已快取的呼叫不會計入限制中。

如要詳細瞭解這些限制對交易和批次寫入作業的影響,請參閱確保原子作業安全指南。

查看通話和價格

使用這些函式會在資料庫中執行讀取作業,這表示即使規則拒絕要求,您仍須支付讀取文件的費用。如需更具體的帳單資訊,請參閱 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();
    }
  }
}

在安全規則中使用函式,可讓規則更易於維護,因為規則的複雜度會隨之增加。

規則不是篩選器

保護資料並開始撰寫查詢後,請注意安全規則並非篩選條件。您無法為集合中的所有文件編寫查詢,並期望 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';
    }
  }
}

已拒絕:這項規則會拒絕下列查詢,因為結果集可能包含 visibility 不是 public 的文件:

網頁
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());
        });
    });

Firestore 安全性規則會根據查詢的潛在結果評估每項查詢,如果查詢可能傳回用戶端無權讀取的文件,就會導致要求失敗。查詢必須遵守安全規則設定的限制。如要進一步瞭解安全性規則和查詢,請參閱安全地查詢資料

後續步驟