您可以使用 Firebase 安全规则根据数据库或存储桶中的现有数据有条件地写入新数据。您还可以编写规则,通过根据正在写入的新数据限制写入来强制执行数据验证。继续阅读以了解有关使用现有数据创建安全条件的规则的更多信息。
在每个部分中选择一个产品以了解有关数据验证规则的更多信息。
对新数据的限制
云端 Firestore
如果您想确保不创建包含特定字段的文档,您可以在allow
条件中包含该字段。例如,如果您想拒绝创建任何包含ranking
字段的文档,您可以在create
条件中禁止它。
service cloud.firestore {
match /databases/{database}/documents {
// Disallow
match /cities/{city} {
allow create: if !("ranking" in request.resource.data)
}
}
}
实时数据库
如果您想确保包含某些值的数据不会添加到您的数据库中,您可以将该值包含在您的规则中并禁止它进行写入。例如,如果您想拒绝任何包含ranking
值的写入,您将禁止对任何具有ranking
值的文档进行写入。
{
"rules": {
// Write is allowed for all paths
".write": true,
// Allows writes only if new data doesn't include a `ranking` child value
".validate": "!newData.hasChild('ranking')
}
}
云储存
如果您想确保不创建包含特定元数据的文件,您可以在allow
条件中包含元数据。例如,如果您想拒绝创建任何包含ranking
元数据的文件,您可以在create
条件中禁止它。
service firebase.storage {
match /b/{bucket}/o {
match /files/{allFiles=**} {
// Disallow
allow create: if !("ranking" in request.resource.metadata)
}
}
}
使用 Firebase 安全规则中的现有数据
云端 Firestore
许多应用程序将访问控制信息存储为数据库中文档的字段。 Cloud Firestore 安全规则可以根据文档数据动态允许或拒绝访问:
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to read data if the document has the 'visibility'
// field set to 'public'
match /cities/{city} {
allow read: if resource.data.visibility == 'public';
}
}
}
resource
变量指的是请求的文档, resource.data
是存储在文档中的所有字段和值的映射。有关resource
变量的更多信息,请参阅参考文档。
写入数据时,您可能希望将传入数据与现有数据进行比较。这使您可以执行一些操作,例如确保字段未更改、字段仅递增 1,或者新值至少在未来一周后出现。在这种情况下,如果您的规则集允许挂起的写入,则request.resource
变量包含文档的未来状态。对于仅修改文档字段子集的update
操作, request.resource
变量将包含操作后的待定文档状态。您可以检查request.resource
中的字段值以防止不需要或不一致的数据更新:
service cloud.firestore {
match /databases/{database}/documents {
// Make sure all cities have a positive population and
// the name is not changed
match /cities/{city} {
allow update: if request.resource.data.population > 0
&& request.resource.data.name == resource.data.name;
}
}
}
实时数据库
在实时数据库中,使用.validate
规则来强制执行数据结构并验证数据的格式和内容。规则在验证.write
规则授予访问权限后运行.validate
规则。
.validate
规则不级联。如果任何验证规则在规则中的任何路径或子路径上失败,则整个写入操作将被拒绝。此外,验证定义仅检查非空值,随后忽略任何删除数据的请求。
考虑以下.validate
规则:
{
"rules": {
// write is allowed for all paths
".write": true,
"widget": {
// a valid widget must have attributes "color" and "size"
// allows deleting widgets (since .validate is not applied to delete rules)
".validate": "newData.hasChildren(['color', 'size'])",
"size": {
// the value of "size" must be a number between 0 and 99
".validate": "newData.isNumber() &&
newData.val() >= 0 &&
newData.val() <= 99"
},
"color": {
// the value of "color" must exist as a key in our mythical
// /valid_colors/ index
".validate": "root.child('valid_colors/' + newData.val()).exists()"
}
}
}
}
使用上述规则向数据库写入请求将产生以下结果:
JavaScript
var ref = db.ref("/widget"); // PERMISSION_DENIED: does not have children color and size ref.set('foo'); // PERMISSION DENIED: does not have child color ref.set({size: 22}); // PERMISSION_DENIED: size is not a number ref.set({ size: 'foo', color: 'red' }); // SUCCESS (assuming 'blue' appears in our colors list) ref.set({ size: 21, color: 'blue'}); // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child('size').set(99);
目标-C
FIRDatabaseReference *ref = [[[FIRDatabase database] reference] child: @"widget"]; // PERMISSION_DENIED: does not have children color and size [ref setValue: @"foo"]; // PERMISSION DENIED: does not have child color [ref setValue: @{ @"size": @"foo" }]; // PERMISSION_DENIED: size is not a number [ref setValue: @{ @"size": @"foo", @"color": @"red" }]; // SUCCESS (assuming 'blue' appears in our colors list) [ref setValue: @{ @"size": @21, @"color": @"blue" }]; // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate [[ref child:@"size"] setValue: @99];
迅速
var ref = FIRDatabase.database().reference().child("widget") // PERMISSION_DENIED: does not have children color and size ref.setValue("foo") // PERMISSION DENIED: does not have child color ref.setValue(["size": "foo"]) // PERMISSION_DENIED: size is not a number ref.setValue(["size": "foo", "color": "red"]) // SUCCESS (assuming 'blue' appears in our colors list) ref.setValue(["size": 21, "color": "blue"]) // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child("size").setValue(99);
爪哇
FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference ref = database.getReference("widget"); // PERMISSION_DENIED: does not have children color and size ref.setValue("foo"); // PERMISSION DENIED: does not have child color ref.child("size").setValue(22); // PERMISSION_DENIED: size is not a number Map<String,Object> map = new HashMap<String, Object>(); map.put("size","foo"); map.put("color","red"); ref.setValue(map); // SUCCESS (assuming 'blue' appears in our colors list) map = new HashMap<String, Object>(); map.put("size", 21); map.put("color","blue"); ref.setValue(map); // If the record already exists and has a color, this will // succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) // will fail to validate ref.child("size").setValue(99);
休息
# PERMISSION_DENIED: does not have children color and size curl -X PUT -d 'foo' \ https://docs-examples.firebaseio.com/rest/securing-data/example.json # PERMISSION DENIED: does not have child color curl -X PUT -d '{"size": 22}' \ https://docs-examples.firebaseio.com/rest/securing-data/example.json # PERMISSION_DENIED: size is not a number curl -X PUT -d '{"size": "foo", "color": "red"}' \ https://docs-examples.firebaseio.com/rest/securing-data/example.json # SUCCESS (assuming 'blue' appears in our colors list) curl -X PUT -d '{"size": 21, "color": "blue"}' \ https://docs-examples.firebaseio.com/rest/securing-data/example.json # If the record already exists and has a color, this will # succeed, otherwise it will fail since newData.hasChildren(['color', 'size']) # will fail to validate curl -X PUT -d '99' \ https://docs-examples.firebaseio.com/rest/securing-data/example/size.json
云储存
在评估规则时,您可能还想评估正在上传、下载、修改或删除的文件的元数据。这使您可以创建复杂而强大的规则,例如只允许上传具有特定内容类型的文件,或者只允许删除大于特定大小的文件。
resource
对象包含键/值对,文件元数据显示在 Cloud Storage 对象中。可以在read
或write
请求时检查这些属性以确保数据完整性。 resource
对象检查 Cloud Storage 存储桶中现有文件的元数据。
service firebase.storage {
match /b/{bucket}/o {
match /images {
match /{allImages=**} {
// Allow reads if a custom 'visibility' field is set to 'public'
allow read: if resource.metadata.visibility == 'public';
}
}
}
}
您还可以在write
请求(例如上传、元数据更新和删除)上使用request.resource
对象。如果允许write
, request.resource
对象会从将要写入的文件中获取元数据。
您可以使用这两个值来防止不需要的或不一致的更新或强制执行应用程序约束,例如文件类型或大小。
service firebase.storage {
match /b/{bucket}/o {
match /images {
// Cascade read to any image type at any path
match /{allImages=**} {
allow read;
}
// Allow write files to the path "images/*", subject to the constraints:
// 1) File is less than 5MB
// 2) Content type is an image
// 3) Uploaded content type matches existing content type
// 4) File name (stored in imageId wildcard variable) is less than 32 characters
match /{imageId} {
allow write: if request.resource.size < 5 * 1024 * 1024
&& request.resource.contentType.matches('image/.*')
&& request.resource.contentType == resource.contentType
&& imageId.size() < 32
}
}
}
}
参考文档中提供了resource
对象中的完整属性列表。