安全地查詢資料

本頁面僅以 建立安全性規則和 請參閱「編寫安全性規則的條件」一文,瞭解 Cloud Firestore 安全性規則與查詢互動。進一步說明 安全性規則會影響您可以撰寫的查詢,並說明如何確保 查詢和安全性規則的限制相同。這個頁面也包含 說明如何編寫安全性規則,以依據查詢允許或拒絕查詢 limitorderBy 等屬性。

規則不是篩選器

編寫擷取文件的查詢時,請記得安全性規則 非篩選條件 - 查詢的任何內容都不行,就是沒有任何項目。為了節省時間和資源 Cloud Firestore 依據查詢的潛在結果集評估查詢內容 而不是所有文件的實際欄位值。如果查詢作業 可能會傳回用戶端無權讀取的文件 整個要求都會失敗

查詢和安全性規則

如下列範例所示,您必須撰寫查詢以符合 並設下安全性規則限制

根據auth.uid保護文件並查詢文件

以下範例說明如何撰寫查詢以擷取文件 並受到安全性規則保護假設資料庫包含 story 份文件:

/story/{storyid}

{
  title: "A Great Story",
  content: "Once upon a time...",
  author: "some_auth_id",
  published: false
}

除了 titlecontent 欄位以外,每份文件都會儲存 用於存取權控管的 authorpublished 欄位。這些範例假設 應用程式使用 Firebase 驗證來設定 author 欄位 附加至文件建立使用者 UID。Firebase 驗證程序也會將 request.auth 變數填入 以及安全性規則

下列安全性規則使用request.authresource.data 變數,限制每個項目的讀取和寫入權限 story 傳送給作者:

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Only the authenticated user who authored the document can read or write
      allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

假設您的應用程式含有向使用者顯示 story 清單的頁面 一般文件。您應該可以使用下列 要填入這個頁面的內容然而,這項查詢將會失敗 包括和安全性規則相同的限制:

無效:查詢限制不符 安全性規則限制

// This query will fail
db.collection("stories").get()

即使目前的使用者實際上是您所有 story 文件。出現這個行為的原因是 Cloud Firestore 會套用你的安全性規則,然後評估查詢 比對結果的「可能」結果集,而不是「實際」屬性 資料庫內的文件如果查詢可以「可能」包含文件 否則查詢就會失敗。

相反地,以下查詢雖然包含相同的查詢,因此能成功執行 對 author 欄位的限製做為安全性規則:

有效:查詢限制符合安全性 規則限制

var user = firebase.auth().currentUser;

db.collection("stories").where("author", "==", user.uid).get()

依據欄位保護文件並查詢文件

為了進一步示範查詢與規則之間的互動,安全性 下方的規則可展開 stories 集合的讀取權限,讓任何使用者 讀取 story 文件中 published 欄位設為 true 的文件。

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Anyone can read a published story; only story authors can read unpublished stories
      allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
      // Only story authors can write
      allow write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

已發布網頁的查詢必須包含與安全性相同的限制 規則:

db.collection("stories").where("published", "==", true).get()

查詢限制 .where("published", "==", true) 可保證 在任何結果中,resource.data.publishedtrue。因此,這個查詢 符合安全性規則,並允許讀取資料。

OR 個查詢

評估邏輯 OR 查詢時 (orinarray-contains-any) 依據規則集,Cloud Firestore 會評估每個比較值 。每個比較值都必須符合安全性規則限制。適用對象 舉例來說 規則:

match /mydocuments/{doc} {
  allow read: if resource.data.x > 5;
}

無效:查詢不保證 所有可能文件的x > 5

// These queries will fail
query(db.collection("mydocuments"),
      or(where("x", "==", 1),
         where("x", "==", 6)
      )
    )

query(db.collection("mydocuments"),
      where("x", "in", [1, 3, 6, 42, 99])
    )

有效:查詢保證 所有可能文件的x > 5

query(db.collection("mydocuments"),
      or(where("x", "==", 6),
         where("x", "==", 42)
      )
    )

query(db.collection("mydocuments"),
      where("x", "in", [6, 42, 99, 105, 200])
    )

評估查詢限制

您的安全性規則也可以根據限制來接受或拒絕查詢。 request.query 變數包含 limitoffset、 和 orderBy 屬性例如您的安全性規則 可以拒絕任何不會限製文件數量上限的查詢 擷取到特定範圍:

allow list: if request.query.limit <= 10;

下列規則集示範如何編寫用以評估安全規則 對查詢設下限制此範例會擴充前一個 stories 同時進行下列變更:

  • 規則集會將讀取規則拆分為 getlist 的規則。
  • get 規則限制擷取單一文件的擷取作業僅限於公開文件, 文件。
  • list 規則套用與 get 相同的限制,但適用於查詢。這項服務 也會檢查查詢限制,然後拒絕任何沒有限制的查詢,或 建議大於 10
  • 規則集會定義 authorOrPublished() 函式來避免程式碼 重複內容
service cloud.firestore {

  match /databases/{database}/documents {

    match /stories/{storyid} {

      // Returns `true` if the requested story is 'published'
      // or the user authored the story
      function authorOrPublished() {
        return resource.data.published == true || request.auth.uid == resource.data.author;
      }

      // Deny any query not limited to 10 or fewer documents
      // Anyone can query published stories
      // Authors can query their unpublished stories
      allow list: if request.query.limit <= 10 &&
                     authorOrPublished();

      // Anyone can retrieve a published story
      // Only a story's author can retrieve an unpublished story
      allow get: if authorOrPublished();

      // Only a story's author can write to a story
      allow write: if request.auth.uid == resource.data.author;
    }

  }
}

集合群組查詢和安全性規則

根據預設,查詢的範圍僅限於單一集合,且會擷取結果 而非該集合。取代為 集合群組查詢 擷取結果集合群組的結果,集合群組內有所有含有 相同的 ID。本節說明如何保護集合群組查詢 安全規則

根據集合群組保護文件並查詢文件

在安全性規則中,您必須明確允許 為集合群組編寫規則,藉此建立集合群組查詢:

  1. 請確定 rules_version = '2'; 是規則集的第一行。集合 群組查詢必須使用 新的週期性萬用字元 {name=**} 安全性行為 規則版本 2
  2. 為產品素材資源集合群組撰寫規則 match /{path=**}/[COLLECTION_ID]/{doc}

舉例來說,假設某個論壇分成 forum 份文件,內含 posts 個子集合:

/forums/{forumid}/posts/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
}

在這個應用程式中,我們開放文章擁有者編輯及閱讀 已通過驗證的使用者:

service cloud.firestore {
  match /databases/{database}/documents {
    match /forums/{forumid}/posts/{post} {
      // Only authenticated users can read
      allow read: if request.auth != null;
      // Only the post author can write
      allow write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

任何經過驗證的使用者都可以擷取任一論壇的貼文:

db.collection("forums/technology/posts").get()

不過,如果您想在所有論壇中顯示目前使用者張貼的訊息,該怎麼做? 您可以使用集合群組查詢來擷取 所有「posts」集合的結果:

var user = firebase.auth().currentUser;

db.collectionGroup("posts").where("author", "==", user.uid).get()

在安全性規則中,您必須允許這項查詢,方法如下: 為 posts 集合群組編寫讀取或清單規則:

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {
    // Authenticated users can query the posts collection group
    // Applies to collection queries, collection group queries, and
    // single document retrievals
    match /{path=**}/posts/{post} {
      allow read: if request.auth != null;
    }
    match /forums/{forumid}/posts/{postid} {
      // Only a post's author can write to a post
      allow write: if request.auth != null && request.auth.uid == resource.data.author;

    }
  }
}

但請注意,這些規則會套用至 ID 為 posts 的所有產品素材資源集合。 不論階層為何舉例來說,這些規則適用於下列所有項目 posts 集合:

  • /posts/{postid}
  • /forums/{forumid}/posts/{postid}
  • /forums/{forumid}/subforum/{subforumid}/posts/{postid}

基於欄位的安全集合群組查詢

如同單一集合查詢,集合群組查詢也必須達到 按照安全性規則設定的限制舉例來說,我們可以新增 published ] 欄位加入每個論壇文章,就和在上述 stories 範例中的做法一樣:

/forums/{forumid}/posts/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
  published: false
}

然後,我們可以根據以下規則為 posts 集合群組撰寫規則 published 狀態以及貼文 author

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {

    // Returns `true` if the requested post is 'published'
    // or the user authored the post
    function authorOrPublished() {
      return resource.data.published == true || request.auth.uid == resource.data.author;
    }

    match /{path=**}/posts/{post} {

      // Anyone can query published posts
      // Authors can query their unpublished posts
      allow list: if authorOrPublished();

      // Anyone can retrieve a published post
      // Authors can retrieve an unpublished post
      allow get: if authorOrPublished();
    }

    match /forums/{forumid}/posts/{postid} {
      // Only a post's author can write to a post
      allow write: if request.auth.uid == resource.data.author;
    }
  }
}

透過這些規則,網頁、Apple 和 Android 用戶端可以進行下列查詢:

  • 任何人都可以擷取論壇中的已發布貼文:

    db.collection("forums/technology/posts").where('published', '==', true).get()
    
  • 所有人都可擷取作者在所有論壇中發布的貼文:

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • 作者可擷取自己在所有平台上已發布和未發布的文章 論壇:

    var user = firebase.auth().currentUser;
    
    db.collectionGroup("posts").where("author", "==", user.uid).get()
    

依集合群組和文件路徑保護文件並查詢文件

在某些情況下,您可能會想根據 。如要建立限制,可以使用 依據欄位保護及查詢文件。

假設應用程式可以追蹤每位使用者的交易 數量:

/users/{userid}/Exchange/{Exchangeid}/transactions/{transaction}

{
  amount: 100,
  exchange: 'some_exchange_name',
  timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
  user: "some_auth_id",
}

請注意 user 欄位。即使我們知道哪個使用者擁有 transaction 則會在文件路徑中複製這項資訊 transaction 文件,是因為可以讓我們進行兩件事:

  • 寫入僅限含有下列內容的文件的集合群組查詢: 特定 /users/{userid}。例如:

    var user = firebase.auth().currentUser;
    // Return current user's last five transactions across all exchanges
    db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
    
  • 對「transactions」集合中的所有查詢強制執行這項限制 群組,因此某個使用者無法擷取其他使用者的 transaction 文件。

我們在安全性規則中強制執行這項限制,而且也會進行資料驗證 ,為 user 欄位指定:

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {

    match /{path=**}/transactions/{transaction} {
      // Authenticated users can retrieve only their own transactions
      allow read: if resource.data.user == request.auth.uid;
    }

    match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
      // Authenticated users can write to their own transactions subcollections
      // Writes must populate the user field with the correct auth id
      allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
    }
  }
}

後續步驟