本頁面以構建安全規則和編寫安全規則條件中的概念為基礎,解釋 Cloud Firestore 安全規則如何與查詢交互。它仔細研究了安全規則如何影響您可以編寫的查詢,並描述瞭如何確保您的查詢使用與安全規則相同的約束。此頁面還介紹瞭如何編寫安全規則以允許或拒絕基於limit
和orderBy
等查詢屬性的查詢。
規則不是過濾器
在編寫檢索文檔的查詢時,請記住安全規則不是過濾器——查詢是全有或全無。為了節省您的時間和資源,Cloud Firestore 會根據查詢的潛在結果集而不是所有文檔的實際字段值來評估查詢。如果查詢可能會返回客戶端無權讀取的文檔,則整個請求將失敗。
查詢和安全規則
如以下示例所示,您必須編寫查詢以符合安全規則的約束。
基於auth.uid
安全和查詢文檔
以下示例演示如何編寫查詢來檢索受安全規則保護的文檔。考慮一個包含story
文檔集合的數據庫:
/故事/{故事編號}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
除了title
和content
字段之外,每個文檔還存儲author
和published
字段以用於訪問控制。這些示例假設應用程序使用Firebase 身份驗證將author
字段設置為創建文檔的用戶的 UID。 Firebase 身份驗證還會在安全規則中填充request.auth
變量。
以下安全規則使用request.auth
和resource.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
集合的讀取訪問權限,以允許任何用戶閱讀published
字段設置為true
的story
文檔。
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.published
對於任何結果都為true
。因此,該查詢滿足安全規則並被允許讀取數據。
OR
查詢
在根據規則集評估邏輯OR
查詢( or
、 in
或array-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
變量包含查詢的limit
、 offset
和orderBy
屬性。例如,您的安全規則可以拒絕任何未將檢索的最大文檔數限制在特定範圍內的查詢:
allow list: if request.query.limit <= 10;
以下規則集演示瞭如何編寫評估對查詢施加的約束的安全規則。此示例通過以下更改擴展了以前的stories
規則集:
- 規則集將讀取規則分為
get
和list
規則。 -
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 的所有集合的集合組中檢索結果。本節介紹如何使用安全規則保護您的集合組查詢。
基於集合組保護和查詢文檔
在您的安全規則中,您必須通過為集合組編寫規則來明確允許集合組查詢:
- 確保
rules_version = '2';
是規則集的第一行。集合組查詢需要安全規則版本 2 的新遞歸通配符{name=**}
行為。 - 使用
match /{path=**}/ [COLLECTION_ID] /{doc}
為您的收藏組編寫規則。
例如,考慮組織成包含posts
子集合的forum
文檔的論壇:
/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
}
然後我們可以根據published
狀態和帖子author
為posts
收集組編寫規則:
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;
}
}
}
使用這些規則,Web、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
}
}
}
下一步
- 有關基於角色的訪問控制的更詳細示例,請參閱保護用戶和組的數據訪問。
- 閱讀安全規則參考。