Trang này dựa trên các khái niệm trong phần Cấu trúc quy tắc bảo mật và Viết điều kiện cho quy tắc bảo mật để giải thích cách bạn có thể sử dụng Cloud Firestore Security Rules để tạo quy tắc cho phép ứng dụng thực hiện các thao tác trên một số trường trong tài liệu nhưng không thực hiện được trên các trường khác.
Đôi khi, bạn có thể muốn kiểm soát các thay đổi đối với một tài liệu không phải ở cấp tài liệu mà ở cấp trường.
Ví dụ: bạn có thể muốn cho phép ứng dụng tạo hoặc thay đổi tài liệu, nhưng không cho phép ứng dụng chỉnh sửa một số trường nhất định trong tài liệu đó. Hoặc bạn có thể muốn thực thi rằng mọi tài liệu mà ứng dụng luôn tạo đều chứa một nhóm trường nhất định. Hướng dẫn này trình bày cách bạn có thể hoàn thành một số nhiệm vụ này bằng cách sử dụng Cloud Firestore Security Rules.
Câu lệnh AI cho tính năng trợ giúp AI
Để được trợ giúp AI, hãy sao chép câu lệnh bên dưới vào tác nhân lập trình AI (như Gemini trong Android Studio, Antigravity, Claude Code, Codex và Cursor). Bạn có thể tuỳ chỉnh câu lệnh và lặp lại với tác nhân. Truy cập vào bài viết Kỹ năng của tác nhân Firebase để biết thông tin chi tiết về cách cài đặt.
Help me use Firebase in my app.
1. Make sure that **Firebase agent skills** are installed and updated. For Claude Code, Codex, or Cursor agents, help me install the Firebase plugin. For other agents, run `npx skills add firebase/agent-skills`. Always try to use **Firebase agent skills** for Firebase-related tasks.
2. Help me write, validate, and deploy Firebase Security Rules to protect my app's Firestore data.
Chỉ cho phép quyền đọc đối với một số trường cụ thể
Thao tác đọc trong Quy tắc được thực hiện ở cấp tài liệu. Bạn có thể truy xuất toàn bộ tài liệu hoặc không truy xuất gì cả. Bạn không thể truy xuất một phần tài liệu. Bạn không thể chỉ sử dụng các quy tắc bảo mật để ngăn người dùng đọc các trường cụ thể trong một tài liệu.
Nếu có một số trường nhất định trong tài liệu mà bạn muốn ẩn với một số người dùng, thì cách tốt nhất là đưa các trường đó vào một tài liệu riêng. Ví dụ: bạn có thể cân nhắc việc tạo một tài liệu trong bộ sưu tập con private như sau:
/employees/{emp_id}
name: "Alice Hamilton",
department: 461,
start_date: <timestamp>
/employees/{emp_id}/private/finances
salary: 80000,
bonus_mult: 1.25,
perf_review: 4.2
Sau đó, bạn có thể thêm các quy tắc bảo mật có các cấp truy cập khác nhau cho hai tập hợp. Trong ví dụ này, chúng ta sử dụng Xác nhận quyền sở hữu xác thực tuỳ chỉnh để nói rằng chỉ những người dùng có xác nhận quyền sở hữu xác thực tuỳ chỉnh role bằng Finance mới có thể xem thông tin tài chính của nhân viên.
service cloud.firestore {
match /databases/{database}/documents {
// Allow any logged in user to view the public employee data
match /employees/{emp_id} {
allow read: if request.resource.auth != null
// Allow only users with the custom auth claim of "Finance" to view
// the employee's financial data
match /private/finances {
allow read: if request.resource.auth &&
request.resource.auth.token.role == 'Finance'
}
}
}
}
Hạn chế các trường khi tạo tài liệu
Quy tắc không có giản đồ, nghĩa là không có quy định hạn chế nào ở cấp cơ sở dữ liệu đối với các trường mà một tài liệu chứa. Mặc dù tính linh hoạt này có thể giúp việc phát triển trở nên dễ dàng hơn, nhưng đôi khi bạn muốn đảm bảo rằng ứng dụng khách chỉ có thể tạo tài liệu chứa các trường cụ thể hoặc không chứa các trường khác.
Bạn có thể tạo các quy tắc này bằng cách kiểm tra phương thức keys của đối tượng request.resource.data. Đây là danh sách tất cả các trường mà ứng dụng
đang cố gắng ghi vào tài liệu mới này. Bằng cách kết hợp nhóm trường này
với các hàm như hasOnly()
hoặc hasAny(),
bạn có thể thêm logic hạn chế các loại tài liệu mà người dùng có thể thêm vào
Quy tắc.
Yêu cầu các trường cụ thể trong tài liệu mới
Giả sử bạn muốn đảm bảo rằng tất cả tài liệu được tạo trong một tập hợp restaurant đều chứa ít nhất một trường name, location và city. Bạn có thể làm việc đó bằng cách gọi hasAll() trên danh sách khoá trong tài liệu mới.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document contains a name
// location, and city field
match /restaurant/{restId} {
allow create: if request.resource.data.keys().hasAll(['name', 'location', 'city']);
}
}
}
Điều này cho phép tạo nhà hàng bằng các trường khác, nhưng đảm bảo rằng tất cả tài liệu do khách hàng tạo đều chứa ít nhất ba trường này.
Cấm các trường cụ thể trong tài liệu mới
Tương tự, bạn có thể ngăn ứng dụng tạo tài liệu chứa các trường cụ thể bằng cách sử dụng hasAny() đối với danh sách các trường bị cấm. Phương thức này có kết quả là true nếu một tài liệu chứa bất kỳ trường nào trong số này, vì vậy, bạn có thể muốn phủ định kết quả để cấm một số trường nhất định.
Ví dụ: trong ví dụ sau, ứng dụng không được phép tạo tài liệu chứa trường average_score hoặc rating_count vì các trường này sẽ được thêm vào bằng lệnh gọi máy chủ sau đó.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document does *not*
// contain an average_score or rating_count field.
match /restaurant/{restId} {
allow create: if (!request.resource.data.keys().hasAny(
['average_score', 'rating_count']));
}
}
}
Tạo danh sách cho phép gồm các trường cho tài liệu mới
Thay vì cấm một số trường nhất định trong tài liệu mới, bạn nên tạo danh sách chỉ gồm những trường được cho phép rõ ràng trong tài liệu mới. Sau đó, bạn có thể sử dụng hàm hasOnly() để đảm bảo rằng mọi tài liệu mới được tạo chỉ chứa các trường này (hoặc một tập hợp con của các trường này) chứ không chứa trường nào khác.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document doesn't contain
// any fields besides the ones listed below.
match /restaurant/{restId} {
allow create: if (request.resource.data.keys().hasOnly(
['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
Kết hợp các trường bắt buộc và không bắt buộc
Bạn có thể kết hợp các thao tác hasAll và hasOnly với nhau trong quy tắc bảo mật để yêu cầu một số trường và cho phép các trường khác. Ví dụ: ví dụ này yêu cầu tất cả tài liệu mới phải chứa các trường name, location và city, đồng thời cho phép các trường address, hours và cuisine (không bắt buộc).
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document has a name,
// location, and city field, and optionally address, hours, or cuisine field
match /restaurant/{restId} {
allow create: if (request.resource.data.keys().hasAll(['name', 'location', 'city'])) &&
(request.resource.data.keys().hasOnly(
['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
Trong trường hợp thực tế, bạn nên chuyển logic này vào một hàm trợ giúp để tránh trùng lặp mã và dễ dàng kết hợp các trường không bắt buộc và bắt buộc vào một danh sách, như sau:
service cloud.firestore {
match /databases/{database}/documents {
function verifyFields(required, optional) {
let allAllowedFields = required.concat(optional);
return request.resource.data.keys().hasAll(required) &&
request.resource.data.keys().hasOnly(allAllowedFields);
}
match /restaurant/{restId} {
allow create: if verifyFields(['name', 'location', 'city'],
['address', 'hours', 'cuisine']);
}
}
}
Hạn chế các trường khi cập nhật
Một phương pháp bảo mật phổ biến là chỉ cho phép ứng dụng chỉnh sửa một số trường chứ không phải tất cả. Bạn không thể thực hiện việc này chỉ bằng cách xem danh sách request.resource.data.keys() được mô tả trong phần trước, vì danh sách này đại diện cho toàn bộ tài liệu như sau khi cập nhật, và do đó sẽ bao gồm các trường mà ứng dụng không thay đổi.
Tuy nhiên, nếu sử dụng hàm diff(), bạn có thể so sánh request.resource.data với đối tượng resource.data, đại diện cho tài liệu trong cơ sở dữ liệu trước khi cập nhật. Thao tác này sẽ tạo một đối tượng mapDiff, là một đối tượng chứa tất cả các thay đổi giữa hai bản đồ khác nhau.
Bằng cách gọi phương thức affectedKeys() trên mapDiff này, bạn có thể đưa ra một tập hợp các trường đã thay đổi trong một lần chỉnh sửa. Sau đó, bạn có thể sử dụng các hàm như
hasOnly()
hoặc hasAny()
để đảm bảo rằng tập hợp này có (hoặc không có) một số mục nhất định.
Ngăn chặn việc thay đổi một số trường
Bằng cách sử dụng phương thức hasAny() trên tập hợp do affectedKeys() tạo, sau đó phủ định kết quả, bạn có thể từ chối mọi yêu cầu của ứng dụng khách cố gắng thay đổi các trường mà bạn không muốn thay đổi.
Ví dụ: bạn có thể muốn cho phép khách hàng cập nhật thông tin về một nhà hàng nhưng không thay đổi điểm trung bình hoặc số lượng bài đánh giá của nhà hàng đó.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Allow the client to update a document only if that document doesn't
// change the average_score or rating_count fields
allow update: if (!request.resource.data.diff(resource.data).affectedKeys()
.hasAny(['average_score', 'rating_count']));
}
}
}
Chỉ cho phép thay đổi một số trường nhất định
Thay vì chỉ định các trường mà bạn không muốn thay đổi, bạn cũng có thể sử dụng hàm hasOnly() để chỉ định danh sách các trường mà bạn muốn thay đổi. Cách này thường được coi là an toàn hơn vì theo mặc định, thao tác ghi vào bất kỳ trường tài liệu mới nào đều không được phép cho đến khi bạn cho phép một cách rõ ràng trong quy tắc bảo mật.
Ví dụ: thay vì không cho phép trường average_score và rating_count, bạn có thể tạo các quy tắc bảo mật chỉ cho phép ứng dụng thay đổi các trường name, location, city, address, hours và cuisine.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Allow a client to update only these 6 fields in a document
allow update: if (request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
Điều này có nghĩa là nếu trong một số lần lặp lại ứng dụng trong tương lai, tài liệu về nhà hàng
bao gồm trường telephone, thì nỗ lực chỉnh sửa trường đó sẽ không thành công
cho đến khi bạn quay lại và thêm trường đó vào danh sách hasOnly() trong quy tắc bảo mật
của mình.
Thực thi loại trường
Một tác động khác của việc Quy tắc không có giản đồ là không có quy định thực thi ở cấp cơ sở dữ liệu về loại dữ liệu có thể được lưu trữ trong các trường cụ thể. Tuy nhiên, bạn có thể thực thi điều này trong các quy tắc bảo mật bằng toán tử is.
Ví dụ: quy tắc bảo mật sau đây thực thi rằng trường score của bài đánh giá phải là số nguyên, các trường headline, content và author_name là chuỗi và review_date là dấu thời gian.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Restaurant rules go here...
match /review/{reviewId} {
allow create: if (request.resource.data.score is int &&
request.resource.data.headline is string &&
request.resource.data.content is string &&
request.resource.data.author_name is string &&
request.resource.data.review_date is timestamp
);
}
}
}
}
Các kiểu dữ liệu hợp lệ cho toán tử is là bool, bytes, float, int, list, latlng, number, path, map, string và timestamp. Toán tử is cũng hỗ trợ các kiểu dữ liệu constraint, duration, set và map_diff, nhưng vì các kiểu dữ liệu này do chính ngôn ngữ quy tắc bảo mật tạo ra chứ không phải do ứng dụng tạo ra, nên bạn hiếm khi sử dụng các kiểu dữ liệu này trong hầu hết các ứng dụng thực tế.
Kiểu dữ liệu list và map không hỗ trợ các đối số chung hoặc đối số kiểu.
Nói cách khác, bạn có thể sử dụng các quy tắc bảo mật để thực thi rằng một trường nhất định chứa danh sách hoặc bản đồ, nhưng bạn không thể thực thi rằng một trường chứa danh sách tất cả số nguyên hoặc tất cả chuỗi.
Tương tự, bạn có thể sử dụng quy tắc bảo mật để thực thi các giá trị loại cho các mục cụ thể trong danh sách hoặc bản đồ (sử dụng ký hiệu dấu ngoặc vuông hoặc tên khoá tương ứng), nhưng không có lối tắt để thực thi các kiểu dữ liệu của tất cả thành viên trong bản đồ hoặc danh sách cùng một lúc.
Ví dụ: các quy tắc sau đây đảm bảo rằng trường tags trong tài liệu chứa một danh sách và mục nhập đầu tiên là một chuỗi. Đồng thời, quy tắc này cũng đảm bảo rằng trường product chứa một bản đồ, trong đó có tên sản phẩm là một chuỗi và số lượng là một số nguyên.
service cloud.firestore {
match /databases/{database}/documents {
match /orders/{orderId} {
allow create: if request.resource.data.tags is list &&
request.resource.data.tags[0] is string &&
request.resource.data.product is map &&
request.resource.data.product.name is string &&
request.resource.data.product.quantity is int
}
}
}
}
Bạn cần thực thi các loại trường khi tạo và cập nhật tài liệu. Do đó, bạn nên cân nhắc việc tạo một hàm trợ giúp mà bạn có thể gọi trong cả phần tạo và cập nhật của quy tắc bảo mật.
service cloud.firestore {
match /databases/{database}/documents {
function reviewFieldsAreValidTypes(docData) {
return docData.score is int &&
docData.headline is string &&
docData.content is string &&
docData.author_name is string &&
docData.review_date is timestamp;
}
match /restaurant/{restId} {
// Restaurant rules go here...
match /review/{reviewId} {
allow create: if reviewFieldsAreValidTypes(request.resource.data) &&
// Other rules may go here
allow update: if reviewFieldsAreValidTypes(request.resource.data) &&
// Other rules may go here
}
}
}
}
Thực thi các loại cho trường tuỳ chọn
Điều quan trọng cần nhớ là việc gọi request.resource.data.foo trên một tài liệu không tồn tại foo sẽ dẫn đến lỗi, và do đó, mọi quy tắc bảo mật thực hiện lệnh gọi đó sẽ từ chối yêu cầu. Bạn có thể xử lý tình huống này bằng cách sử dụng phương thức get trên request.resource.data. Phương thức get cho phép bạn cung cấp đối số mặc định cho trường mà bạn đang truy xuất từ bản đồ nếu trường đó không tồn tại.
Ví dụ: nếu tài liệu đánh giá cũng chứa một trường photo_url không bắt buộc và một trường tags không bắt buộc mà bạn muốn xác minh lần lượt là chuỗi và danh sách, thì bạn có thể thực hiện việc này bằng cách viết lại hàm reviewFieldsAreValidTypes thành một hàm như sau:
function reviewFieldsAreValidTypes(docData) {
return docData.score is int &&
docData.headline is string &&
docData.content is string &&
docData.author_name is string &&
docData.review_date is timestamp &&
docData.get('photo_url', '') is string &&
docData.get('tags', []) is list;
}
Thao tác này sẽ từ chối các tài liệu có tags nhưng không phải là danh sách, đồng thời vẫn cho phép các tài liệu không chứa trường tags (hoặc photo_url).
Không bao giờ cho phép ghi một phần
Một lưu ý cuối cùng về Cloud Firestore Security Rules là chúng cho phép ứng dụng thay đổi tài liệu hoặc từ chối toàn bộ nội dung chỉnh sửa. Bạn không thể tạo quy tắc bảo mật chấp nhận ghi vào một số trường trong tài liệu của mình trong khi từ chối các trường khác trong cùng một thao tác.