หน้านี้ต่อยอดจากแนวคิดใน
การกำหนดโครงสร้างกฎการรักษาความปลอดภัยและ
การเขียนเงื่อนไขสำหรับกฎการรักษาความปลอดภัยเพื่ออธิบายวิธี
Cloud Firestore Security Rules โต้ตอบกับคำค้นหา ดูรายละเอียดวิธี
กฎความปลอดภัยจะมีผลต่อข้อความค้นหาที่คุณสามารถเขียนและอธิบายวิธีตรวจสอบให้แน่ใจว่า
จะใช้ข้อจำกัดเดียวกันกับกฎความปลอดภัยของคุณ หน้านี้ยังอธิบายวิธีเขียนกฎความปลอดภัยเพื่ออนุญาตหรือปฏิเสธการค้นหาตามพร็อพเพอร์ตี้การค้นหา เช่น 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
}
}
}
ขั้นตอนถัดไป
- ดูตัวอย่างการควบคุมการเข้าถึงตามบทบาทโดยละเอียดได้ที่การรักษาความปลอดภัยให้กับสิทธิ์เข้าถึงข้อมูลของผู้ใช้และกลุ่ม
- โปรดอ่านข้อมูลอ้างอิงกฎความปลอดภัย