หน้านี้สร้างขึ้นจากแนวคิดใน การจัดโครงสร้างกฎความปลอดภัย และ เงื่อนไขการเขียนสำหรับกฎความปลอดภัย เพื่ออธิบายว่ากฎความปลอดภัยของ Cloud Firestore โต้ตอบกับการสืบค้นอย่างไร โดยจะพิจารณาอย่างละเอียดยิ่งขึ้นว่ากฎความปลอดภัยส่งผลต่อการสืบค้นที่คุณเขียนได้อย่างไร และอธิบายวิธีการตรวจสอบให้แน่ใจว่าการสืบค้นของคุณใช้ข้อจำกัดเดียวกันกับกฎความปลอดภัยของคุณ หน้านี้ยังอธิบายวิธีเขียนกฎความปลอดภัยเพื่ออนุญาตหรือปฏิเสธการสืบค้นตามคุณสมบัติการสืบค้น เช่น limit
และ orderBy
กฎไม่ใช่ตัวกรอง
เมื่อเขียนการสืบค้นเพื่อดึงเอกสาร โปรดจำไว้ว่ากฎความปลอดภัยไม่ใช่ตัวกรอง การสืบค้นจะเป็นทั้งหมดหรือไม่มีเลย เพื่อประหยัดเวลาและทรัพยากรของคุณ Cloud Firestore จะประเมินคำสืบค้นโดยเทียบกับชุดผลลัพธ์ที่เป็นไปได้ แทนที่จะประเมินค่าช่องจริงสำหรับเอกสารทั้งหมดของคุณ หากแบบสอบถามอาจส่งคืนเอกสารที่ไคลเอ็นต์ไม่มีสิทธิ์ในการอ่าน คำขอทั้งหมดจะล้มเหลว
แบบสอบถามและกฎความปลอดภัย
ดังตัวอย่างด้านล่างที่แสดงให้เห็น คุณต้องเขียนคำถามของคุณให้สอดคล้องกับข้อจำกัดของกฎความปลอดภัยของคุณ
รักษาความปลอดภัยและสืบค้นเอกสารตาม auth.uid
ตัวอย่างต่อไปนี้สาธิตวิธีการเขียนแบบสอบถามเพื่อดึงเอกสารที่ได้รับการป้องกันตามกฎความปลอดภัย พิจารณาฐานข้อมูลที่ประกอบด้วยชุดของเอกสาร story
:
/ เรื่องราว / {storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
นอกจาก title
และ content
แล้ว แต่ละเอกสารยังจัดเก็บฟิลด์ author
และฟิลด์ published
เพื่อใช้สำหรับการควบคุมการเข้าถึง ตัวอย่างเหล่านี้ถือว่าแอปใช้ Firebase Authentication เพื่อตั้งค่าช่อง 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
เพื่อให้ผู้ใช้สามารถอ่านเอกสาร 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.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';
คือบรรทัดแรกของชุดกฎของคุณ การสืบค้นกลุ่มคอลเลกชันจำเป็นต้องมีพฤติกรรม ตัวแทนแบบเรียกซ้ำ{name=**}
ของกฎความปลอดภัยเวอร์ชัน 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;
}
}
}
อย่างไรก็ตาม โปรดทราบว่ากฎเหล่านี้จะนำไปใช้กับคอลเลกชันทั้งหมดที่มีการ posts
ID โดยไม่คำนึงถึงลำดับชั้น ตัวอย่างเช่น กฎเหล่านี้ใช้กับคอลเลกชัน 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
}
}
}
ขั้นตอนถัดไป
- สำหรับตัวอย่างโดยละเอียดเพิ่มเติมของการควบคุมการเข้าถึงตามบทบาท โปรดดูที่ การรักษาความปลอดภัยการเข้าถึงข้อมูลสำหรับผู้ใช้และกลุ่ม
- อ่าน การอ้างอิงกฎความปลอดภัย