Trang này dựa trên các khái niệm trong
Cấu trúc Quy tắc bảo mật và
Điều kiện viết cho quy tắc bảo mật để giải thích cách
Cloud Firestore Security Rules tương tác với các truy vấn. Cần xem xét kỹ hơn cách
quy tắc bảo mật ảnh hưởng đến các truy vấn mà bạn có thể viết và mô tả cách đảm bảo
truy vấn nào cũng sử dụng các quy tắc ràng buộc giống như quy tắc bảo mật của bạn. Trang này cũng
mô tả cách viết các quy tắc bảo mật để cho phép hoặc từ chối các truy vấn dựa trên truy vấn
các thuộc tính như limit
và orderBy
.
Quy tắc không phải là bộ lọc
Khi viết truy vấn để truy xuất tài liệu, hãy nhớ rằng quy tắc bảo mật không phải bộ lọc—truy vấn là tất cả hoặc không có gì. Để tiết kiệm thời gian và tài nguyên, Cloud Firestore đánh giá truy vấn dựa trên nhóm kết quả tiềm năng thay vì các giá trị trường thực tế cho tất cả tài liệu của bạn. Nếu một truy vấn có thể có thể trả về tài liệu mà khách hàng không có quyền đọc, toàn bộ yêu cầu sẽ không thành công.
Cụm từ tìm kiếm và quy tắc bảo mật
Như các ví dụ dưới đây minh hoạ, bạn phải viết truy vấn sao cho phù hợp với các quy tắc ràng buộc của quy tắc bảo mật.
Bảo mật và truy vấn tài liệu dựa trên auth.uid
Ví dụ sau đây minh hoạ cách viết truy vấn để truy xuất các tài liệu được bảo vệ bằng quy tắc bảo mật. Hãy xem xét cơ sở dữ liệu chứa tập hợp
story
tài liệu:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time...",
author: "some_auth_id",
published: false
}
Ngoài các trường title
và content
, mỗi tài liệu còn lưu trữ
Các trường author
và published
để dùng để kiểm soát quyền truy cập. Những ví dụ này giả định
ứng dụng sử dụng tính năng Xác thực Firebase để đặt trường author
cho UID của người dùng đã tạo tài liệu. Firebase
Việc xác thực cũng điền sẵn biến request.auth
trong
các quy tắc bảo mật.
Quy tắc bảo mật sau đây sử dụng request.auth
và
Biến resource.data
để hạn chế quyền đọc và ghi đối với mỗi biến
story
gửi cho tác giả:
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;
}
}
}
Giả sử rằng ứng dụng của bạn bao gồm một trang hiển thị cho người dùng danh sách story
tài liệu mà họ tạo ra. Bạn có thể thấy rằng mình có thể sử dụng
để điền trang này. Tuy nhiên, truy vấn này sẽ không thành công vì không
đưa vào các quy tắc ràng buộc tương tự như quy tắc bảo mật của bạn:
Không hợp lệ: Các điều kiện ràng buộc đối với truy vấn không khớp các điều kiện ràng buộc đối với quy tắc bảo mật
// This query will fail
db.collection("stories").get()
Truy vấn không thành công ngay cả khi người dùng hiện tại thực sự là tác giả của mọi
story
tài liệu. Nguyên nhân của hành vi này là khi
Cloud Firestore sẽ áp dụng các quy tắc bảo mật của bạn, nó sẽ đánh giá truy vấn
dựa trên nhóm kết quả tiềm năng của kết quả đó, chứ không dựa trên thuộc tính thực tế của
trong cơ sở dữ liệu của bạn. Nếu một truy vấn có thể bao gồm các tài liệu vi phạm quy tắc bảo mật của bạn, thì truy vấn đó sẽ không thành công.
Ngược lại, truy vấn sau đây thành công do truy vấn bao gồm cùng một
ràng buộc trên trường author
làm quy tắc bảo mật:
Hợp lệ: Các quy tắc ràng buộc truy vấn khớp với các quy tắc ràng buộc về bảo mật
var user = firebase.auth().currentUser;
db.collection("stories").where("author", "==", user.uid).get()
Bảo mật và truy vấn tài liệu dựa trên trường
Để minh hoạ rõ hơn về sự tương tác giữa các truy vấn và quy tắc, tính năng bảo mật
các quy tắc bên dưới mở rộng quyền đọc cho tập hợp stories
để cho phép mọi người dùng
đọc tài liệu story
trong đó trường published
được đặt thành 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;
}
}
}
Truy vấn cho các trang đã xuất bản phải có cùng những quy tắc ràng buộc như bảo mật quy tắc:
db.collection("stories").where("published", "==", true).get()
Quy tắc ràng buộc truy vấn .where("published", "==", true)
đảm bảo rằng
resource.data.published
là true
cho mọi kết quả. Do đó, truy vấn này đáp ứng các quy tắc bảo mật và được phép đọc dữ liệu.
OR
cụm từ tìm kiếm
Khi đánh giá một truy vấn OR
logic (or
, in
hoặc array-contains-any
)
dựa trên một bộ quy tắc, Cloud Firestore sẽ đánh giá từng giá trị so sánh
riêng biệt. Mỗi giá trị so sánh phải đáp ứng các hạn chế của quy tắc bảo mật. Để
ví dụ: đối với
quy tắc sau:
match /mydocuments/{doc} {
allow read: if resource.data.x > 5;
}
Không hợp lệ: Truy vấn không đảm bảo rằng
x > 5
đối với tất cả các tài liệu có thể áp dụng
// 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])
)
Hợp lệ: Truy vấn đảm bảo rằng
x > 5
đối với tất cả các tài liệu có thể áp dụng
query(db.collection("mydocuments"),
or(where("x", "==", 6),
where("x", "==", 42)
)
)
query(db.collection("mydocuments"),
where("x", "in", [6, 42, 99, 105, 200])
)
Đánh giá các hạn chế đối với truy vấn
Quy tắc bảo mật của bạn cũng có thể chấp nhận hoặc từ chối truy vấn dựa trên những ràng buộc tương ứng.
Biến request.query
chứa limit
, offset
,
và orderBy
của một truy vấn. Ví dụ: quy tắc bảo mật của bạn có thể từ chối mọi truy vấn không giới hạn số lượng tài liệu tối đa được truy xuất trong một phạm vi nhất định:
allow list: if request.query.limit <= 10;
Quy tắc sau đây minh hoạ cách viết các quy tắc bảo mật đánh giá các quy tắc ràng buộc được đặt trên truy vấn. Ví dụ này mở rộng stories
trước
bộ quy tắc mới với những thay đổi sau:
- Bộ quy tắc này phân tách quy tắc đọc thành các quy tắc cho
get
vàlist
. - Quy tắc
get
hạn chế truy xuất từng tài liệu vào các tài liệu công khai hoặc tài liệu mà người dùng đã tạo. - Quy tắc
list
áp dụng các quy định hạn chế tương tự nhưget
nhưng dành cho truy vấn. Nó cũng kiểm tra giới hạn truy vấn, sau đó từ chối bất kỳ truy vấn nào không có giới hạn hoặc có lớn hơn 10. - Bộ quy tắc xác định một hàm
authorOrPublished()
để tránh trùng lặp mã.
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;
}
}
}
Truy vấn của nhóm thu thập và quy tắc bảo mật
Theo mặc định, các truy vấn nằm trong phạm vi một tập hợp và truy xuất kết quả chỉ trong bộ sưu tập đó. Bằng truy vấn nhóm thu thập, bạn có thể truy xuất kết quả từ nhóm bộ sưu tập bao gồm tất cả các bộ sưu tập có phương thức cùng một mã nhận dạng. Phần này mô tả cách bảo vệ các truy vấn của nhóm thu thập thông qua các quy tắc bảo mật.
Bảo mật và truy vấn tài liệu dựa trên các nhóm thu thập
Trong quy tắc bảo mật, bạn phải cho phép rõ ràng các truy vấn nhóm bộ sưu tập bằng cách viết một quy tắc cho nhóm bộ sưu tập:
- Đảm bảo
rules_version = '2';
là dòng đầu tiên trong bộ quy tắc. Truy vấn nhóm bộ sưu tập yêu cầu hành vi ký tự đại diện đệ quy mới{name=**}
của quy tắc bảo mật phiên bản 2. - Viết quy tắc cho nhóm bộ sưu tập bằng cách sử dụng
match /{path=**}/[COLLECTION_ID]/{doc}
.
Ví dụ: xem xét một diễn đàn được sắp xếp thành forum
tài liệu có chứa
posts
tập hợp con:
/forums/{forumid}/post/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
}
Trong ứng dụng này, chúng tôi làm cho bài đăng có thể chỉnh sửa bởi chủ sở hữu của chúng và có thể đọc được bằng cách người dùng đã xác thực:
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;
}
}
}
Mọi người dùng đã xác thực đều có thể truy xuất bài đăng của bất kỳ diễn đàn nào:
db.collection("forums/technology/posts").get()
Nhưng nếu bạn muốn hiển thị bài đăng của họ cho người dùng hiện tại trên tất cả diễn đàn thì sao?
Bạn có thể sử dụng truy vấn nhóm bộ sưu tập để truy xuất
kết quả từ tất cả posts
bộ sưu tập:
var user = firebase.auth().currentUser;
db.collectionGroup("posts").where("author", "==", user.uid).get()
Trong các quy tắc bảo mật, bạn phải cho phép truy vấn này bằng cách viết quy tắc đọc hoặc danh sách cho nhóm bộ sưu tập 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;
}
}
}
Tuy nhiên, hãy lưu ý rằng các quy tắc này sẽ áp dụng cho tất cả các bộ sưu tập có mã posts
,
bất kể hệ phân cấp. Ví dụ: các quy tắc này áp dụng cho tất cả các
posts
bộ sưu tập:
/posts/{postid}
/forums/{forumid}/posts/{postid}
/forums/{forumid}/subforum/{subforumid}/posts/{postid}
Các truy vấn của nhóm thu thập bảo mật dựa trên một trường
Giống như các truy vấn một bộ sưu tập, các truy vấn nhóm bộ sưu tập cũng phải đáp ứng
do các quy tắc bảo mật của bạn thiết lập. Ví dụ: chúng ta có thể thêm published
vào mỗi bài đăng trên diễn đàn như chúng ta đã làm trong ví dụ stories
ở trên:
/forums/{forumid}/posts/{postid}
{
author: "some_auth_id",
authorname: "some_username",
content: "I just read a great story.",
published: false
}
Sau đó, chúng ta có thể viết các quy tắc cho nhóm bộ sưu tập posts
dựa trên
Trạng thái published
và bài đăng 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;
}
}
}
Với các quy tắc này, ứng dụng Web, Apple và Android có thể thực hiện các truy vấn sau:
Bất cứ ai cũng có thể truy xuất bài đăng đã xuất bản trong diễn đàn:
db.collection("forums/technology/posts").where('published', '==', true).get()
Bất cứ ai cũng có thể truy xuất bài đăng đã xuất bản của tác giả trên tất cả các diễn đàn:
db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
Tác giả có thể truy xuất tất cả bài đăng đã xuất bản và chưa xuất bản của họ trên tất cả diễn đàn:
var user = firebase.auth().currentUser; db.collectionGroup("posts").where("author", "==", user.uid).get()
Bảo mật và truy vấn tài liệu dựa trên đường dẫn đến nhóm thu thập và tài liệu
Trong một số trường hợp, bạn có thể muốn hạn chế các truy vấn nhóm thu thập dựa trên đường dẫn tài liệu. Để tạo những hạn chế này, bạn có thể sử dụng các kỹ thuật tương tự cho bảo mật và truy vấn tài liệu dựa trên trường.
Hãy cân nhắc một ứng dụng theo dõi giao dịch của từng người dùng giữa một số sàn giao dịch chứng khoán và tiền mã hoá:
/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",
}
Hãy lưu ý trường user
. Mặc dù chúng ta biết người dùng nào sở hữu transaction
khỏi đường dẫn của tài liệu, chúng tôi sẽ sao chép thông tin này trong mỗi
transaction
tài liệu vì công cụ này cho phép chúng ta làm 2 việc:
Ghi các truy vấn nhóm bộ sưu tập được giới hạn cho các tài liệu bao gồm một
/users/{userid}
cụ thể trong đường dẫn tài liệu. Ví dụ: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)
Thực thi quy định hạn chế này đối với tất cả truy vấn trên tập hợp
transactions
để một người dùng không thể truy xuất tài liệutransaction
của người dùng khác.
Chúng tôi thực thi quy định hạn chế này trong các quy tắc bảo mật của mình và thêm hoạt động xác thực dữ liệu
cho trường 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
}
}
}
Các bước tiếp theo
- Để biết ví dụ chi tiết hơn về việc kiểm soát quyền truy cập dựa trên vai trò, hãy xem phần Bảo mật dữ liệu Quyền truy cập cho người dùng và nhóm.
- Đọc tài liệu tham khảo về quy tắc bảo mật.