หน้านี้ต่อยอดจากแนวคิดในการสร้างกฎการรักษาความปลอดภัยและการเขียนเงื่อนไขสำหรับกฎความปลอดภัยเพื่ออธิบายวิธีที่กฎการรักษาความปลอดภัยของ Cloud Firestore โต้ตอบกับคำค้นหา ซึ่งจะเจาะลึกว่ากฎความปลอดภัยส่งผลต่อการค้นหาที่คุณเขียนอย่างไร และอธิบายวิธีตรวจสอบว่าคำค้นหาใช้ข้อจำกัดเดียวกันกับกฎความปลอดภัยอย่างไร หน้านี้ยังอธิบายวิธีเขียนกฎความปลอดภัยเพื่ออนุญาตหรือปฏิเสธการค้นหาโดยอิงตามพร็อพเพอร์ตี้ของการค้นหา เช่น limit
และ orderBy
ด้วย
กฎไม่ใช่ตัวกรอง
เมื่อเขียนคำค้นหาเพื่อเรียกเอกสาร โปรดทราบว่ากฎความปลอดภัยไม่ใช่ตัวกรอง เนื่องจากคำค้นหาเป็นคำค้นหาทั้งหมดหรือไม่มีเลย Cloud Firestore จะประเมินชุดผลลัพธ์ที่เป็นไปได้แทนค่าในช่องจริงสำหรับเอกสารทั้งหมด เพื่อช่วยคุณประหยัดเวลาและทรัพยากร หากการค้นหาอาจแสดงเอกสารที่ไคลเอ็นต์ไม่มีสิทธิ์อ่าน คำขอทั้งหมดจะล้มเหลว
คำค้นหาและกฎความปลอดภัย
ดังที่แสดงในตัวอย่างด้านล่าง คุณต้องเขียนคำค้นหาให้เหมาะกับข้อจำกัดของกฎความปลอดภัย
รักษาความปลอดภัยและค้นหาเอกสารตาม auth.uid
ตัวอย่างต่อไปนี้แสดงวิธีการเขียนการค้นหาเพื่อเรียกเอกสารที่มีการป้องกันโดยกฎความปลอดภัย ลองพิจารณาฐานข้อมูลที่มีคอลเล็กชันเอกสาร story
รายการ
/stories/{storyid}
{
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
เพื่อให้ผู้ใช้อ่านเอกสาร 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;
}
}
}
การค้นหาและกฎความปลอดภัยของกลุ่มคอลเล็กชัน
โดยค่าเริ่มต้น การค้นหาจะกำหนดขอบเขตไว้เป็นคอลเล็กชันเดียวและจะดึงผลการค้นหาจากคอลเล็กชันนั้นเท่านั้น เมื่อใช้การค้นหากลุ่มคอลเล็กชัน คุณจะดึงผลลัพธ์จากกลุ่มคอลเล็กชันที่ประกอบด้วยคอลเล็กชันทั้งหมดที่มีรหัสเดียวกันได้ ส่วนนี้จะอธิบายวิธีรักษาความปลอดภัยของการค้นหากลุ่มคอลเล็กชันโดยใช้กฎความปลอดภัย
รักษาความปลอดภัยและค้นหาเอกสารตามกลุ่มคอลเล็กชัน
ในกฎความปลอดภัย คุณต้องอนุญาตการค้นหากลุ่มคอลเล็กชันอย่างชัดแจ้งด้วยการเขียนกฎสำหรับกลุ่มคอลเล็กชัน ดังนี้
- ตรวจสอบว่า
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
ไม่ว่าลำดับชั้นใดก็ตาม ตัวอย่างเช่น กฎเหล่านี้ใช้กับคอลเล็กชัน 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
แต่ละฉบับ เนื่องจากช่วยให้เราทำ 2 สิ่งต่อไปนี้ได้
เขียนการค้นหากลุ่มคอลเล็กชันที่จำกัดไว้สำหรับเอกสารที่มี
/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
}
}
}
ขั้นตอนถัดไป
- ดูตัวอย่างโดยละเอียดเพิ่มเติมเกี่ยวกับการควบคุมการเข้าถึงตามบทบาทได้ที่การรักษาความปลอดภัยการเข้าถึงข้อมูลสำหรับผู้ใช้และกลุ่ม
- โปรดอ่านข้อมูลอ้างอิงกฎความปลอดภัย