数据验证

您可以根据数据库或存储桶中的现有数据,使用 Firebase 安全规则有条件地写入新数据。您还可以编写用来强制执行数据验证的规则,方法是根据写入的新数据来限制编写操作。继续阅读以详细了解有关使用现有数据创建安全条件的规则。

选择每个部分中的产品,详细了解数据验证规则。

对新数据的限制

Cloud Firestore

如果要确保包含特定字段的文档尚未创建,您可以在 allow 条件中包含该字段。例如,如果要拒绝创建包含 ranking 字段的所有文档,您可以在 create 条件中禁止它。

  service cloud.firestore {
    match /databases/{database}/documents {
      // Disallow
      match /cities/{city} {
        allow create: if !("ranking" in request.resource.data)
      }
    }
  }

Realtime Database

如果要确保包含特定值的数据未添加到数据库中,您必须在规则中包含该值,并禁止对它进行写入。例如,如果要拒绝包含 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')
    }
  }

Cloud Storage

如果要确保未创建包含特定元数据的文件,您可以在 allow 条件中包含元数据。例如,如果要拒绝创建包含 ranking 元数据的所有文件,则可以在 create 条件中禁止它。

  service firebase.storage {
    match /b/{bucket}/o {
      match /files/{allFiles=**} {
      // Disallow
        allow create: if !("ranking" in request.resource.metadata)
      }
    }
  }

在 Firebase 安全规则中使用现有数据

Cloud 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;
      }
    }
  }

Realtime Database

在 Realtime Database 中,使用 .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);
Objective-C
注意:此 Firebase 产品不适用于轻 App 目标。
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];
Swift
注意:此 Firebase 产品不适用于轻 App 目标。
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);
Java
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);
REST
# 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

Cloud Storage

对规则进行求值时,您可能还想要对正在上传、下载、修改或删除的文件的元数据进行求值。这样一来,您就可以创建功能强大的复杂规则来执行任务,例如仅允许上传包含特定内容类型的文件,或仅允许删除超过特定大小的文件。

resource 对象包含具有在 Cloud Storage 对象中显示的文件元数据的键值对。可以在 readwrite 请求中检查这些属性,以确保数据完整性。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 对象(例如上传、元数据更新及删除)。如果允许 writerequest.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 对象中的完整属性列表。