Firebase Database Security Rules API

规则:类型

.read

授予客户端对 Firebase Realtime Database 位置的读取权限。

.read 规则是一种安全规则,可授予客户端对 Firebase Realtime Database 位置的读取权限。例如:

 ".read": "auth != null && auth.provider == 'twitter'"

.read 规则的值是一个字符串,它被评估为 JavaScript 表达式语法的子集,并进行了几项行为变更,以提高清晰度和正确性。授予读取某个营业地点的权限的 .read 规则也将允许读取该营业地点的任何下级,即使下级拥有自己的 .read 规则失败也是如此。

.read 规则可以访问除 newData 之外的所有 Firebase Realtime Database 规则变量

.write

授予对 Firebase Realtime Database 位置的客户端写入权限。

.write 规则是一种安全规则,可授予客户端对 Firebase Realtime Database 位置的写入权限。例如:

".write": "auth != null && auth.token.isAdmin == true"

.write 规则的值是一个字符串,它被评估为 JavaScript 表达式语法的子集,并进行了几项行为变更,以提高清晰度和正确性。用于授予写入某个位置的权限的 .write 规则也将允许对该位置的任何下级执行写入操作,即使下级式自身的 .write 规则失败也是如此。

.write 规则可以访问 Firebase Realtime Database 的所有规则变量

.validate

.write 规则授予访问权限后使用,以确保要写入的数据符合特定架构。

.write 规则授予访问权限后,系统会使用 .validate 规则来确保写入的数据符合特定标准。除了授予访问权限的 .write 之外,所有相关的 .validate 规则都必须成功执行,然后才能执行写入操作。例如:

".validate": "newData.hasChildren(['name', 'age'])"

.validate 规则的值是一个字符串,它被评估为 JavaScript 表达式语法的子集,并进行了几项行为变更,以提高清晰度和正确性。

.validate 规则可以访问 Firebase Realtime Database 的所有规则变量

.indexOn

告知 Firebase Realtime Database 您希望将数据编入索引的键,从而提高查询性能。

.indexOn 规则会告知 Firebase Realtime Database 服务器将您数据中的特定键编入索引,以提高查询性能。例如,假设某个数据库中有恐龙数据集合,我们可以通过添加此规则来指示 Firebase Realtime Database 在查询从服务器返回之前针对查询进行优化:

{
  "rules": {
    "dinosaurs": {
      ".indexOn": ["height", "length"]
    }
  }
}

如需详细了解 .indexOn 规则,请参阅安全指南中关于将数据编入索引的部分。

规则:变量

auth

包含令牌载荷的变量(如果客户端已通过身份验证)或 null 如果客户端未通过身份验证,则会发生此错误。

Firebase Realtime Database 让您可以轻松向多个内置提供程序进行身份验证,并为它们生成身份验证令牌。用户使用某个内置提供程序进行身份验证后,auth 变量将包含以下内容:

字段 说明
provider 所用的身份验证方法(例如“password”“anonymous”“facebook”“github”“google”或“twitter”)。
uid 唯一用户 ID,保证在所有提供程序中都是唯一的。
token Firebase Auth ID 令牌的内容。请参阅 auth.token

举个例子,我们可以设置如下规则,允许用户创建评论,只要用户将他们的用户 ID 与评论存储在一起即可:

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && newData.child('user_id').val() == auth.uid"
    }
  }
}

我们还可以创建如下规则,允许通过 Facebook 登录的用户创建评论:

{
  "rules": {
    ".read": true,
    "$comment": {
      ".write": "!data.exists() && auth.provider == 'facebook'"
    }
  }
}

auth.token 。

包含 Firebase Auth ID 令牌内容的变量。

该令牌包含以下部分或全部键:

字段 说明
email 与账号关联的电子邮件地址(如果存在)。
email_verified 如果用户已验证他们可以访问 email 地址,则为 true。某些提供方会自动验证他们拥有的电子邮件地址。
phone_number 与账号关联的电话号码(如果有)。
name 用户的显示名(如果已设置)。
sub 用户的 Firebase UID。此 UID 在项目中是唯一的。
firebase.identities 与此用户账号关联的所有身份的字典。字典的键可以是以下任一值:emailphonegoogle.comfacebook.comgithub.comtwitter.com。字典的值是与账号关联的每个身份提供方的唯一标识符的数组。例如,auth.token.firebase.identities["google.com"][0] 包含与该账号关联的第一个 Google 用户 ID。
firebase.sign_in_provider 用于获取此令牌的登录服务提供方。可以是以下任一字符串:custompasswordphoneanonymousgoogle.comfacebook.comgithub.comtwitter.com
firebase.tenant 与账号关联的租户 ID(如有)。例如 tenant2-m6tyz

如果使用自定义身份验证,auth.token 还包含任何自定义 声明。

所有这些值都可以在规则中使用。例如,要限制对与 gmail.com 地址关联的 Google 账号的访问权限,我们可以添加以下规则:

{
  "rules": {
    ".read": "auth != null",
    "gmailUsers": {
      "$uid": {
        ".write": "auth.token.email_verified == true && auth.token.email.matches(/.*@gmail.com$/)"
      }
    }
  }
}

为完整起见,auth.token 中也加入了以下字段,但它们对规则可能没什么用。

字段 说明
iss 令牌的颁发者。
aud 令牌的目标设备。
auth_time 用户上次使用接收令牌的设备使用凭据进行身份验证的时间。
iat 令牌的颁发时间。
exp 令牌的过期时间。

$location

一个变量,可用于引用之前在规则结构中使用的 $location 的键。

如果您的规则结构中有 $location,您可以在规则表达式中使用匹配的 $ 变量来获取被读取或写入的实际子级的名称。因此,假设我们希望向每位用户授予对其自己的 /users/<user> 位置的读写权限。我们可以使用:

{
  "rules": {
    "users": {
      "$user": {
        ".read": "auth.uid === $user",
        ".write": "auth.uid === $user"
      }
    }
  }
}

当客户端尝试访问 /users/barney 时,$user 默认位置将与等于“barney”的 $user 匹配。因此,.read 规则将检查 auth.uid === 'barney'。因此,只有在使用“barney” uid 对客户端进行身份验证时,读取 /users/barney 才会成功。

现在

包含根据 Firebase Realtime Database 服务器自 Unix 纪元起经过的毫秒数。

now 变量包含从 UNIX 纪元开始计算的毫秒数(根据 Firebase Realtime Database 服务器计算)。例如,您可以使用此字段来验证用户的 created 时间从未设置为将来的时间:

{
  "rules": {
    "users": {
      "$user": {
        "created": {
          ".validate": "newData.val() < now"
        }
      }
    }
  }
}

root

与 Firebase Realtime Database 根目录下的当前数据对应的 RuleDataSnapshot

根变量为您提供与 Firebase Realtime Database 根目录下的当前数据相对应的 RuleDataSnapshot。您可以使用它在规则表达式中读取数据库中的任何数据。例如,如果我们希望仅在用户的 /users/<id>/active 设置为 true 时才允许用户读取 /comments,则可以使用以下代码:

{
  "rules": {
    "comments": {
      ".read": "root.child('users').child(auth.uid).child('active').val() == true"
    }
  }
}

然后,如果 /users/barney/active 包含值 true,则用户使用“barney” UID 对用户进行身份验证可以写入 /comments 节点。

数据

一个 RuleDataSnapshot,对应 Firebase Realtime Database 中当前正在执行的规则所在位置的数据。

数据变量为您提供 RuleDataSnapshot,与当前正在执行的规则的数据库位置中的当前数据相对应(与 root 相反,根相反, root 可返回数据库根的数据)。

例如,如果您想在 /users/<user>/public 设置为 true 的情况下允许任何客户端访问 /users/<user>,则可以使用以下代码:

{
  "rules": {
    "users": {
      "$user": {
        ".read": "data.child('public').val() == true"
      }
    }
  }
}

数据变量在 .read.write.validate 条规则。

newData

与允许写入时产生的数据对应的 RuleDataSnapshot

对于 .write.validate 规则,newData 变量会为您提供 RuleDataSnapshot,对应于允许写入时产生的数据(这是现有数据与正在写入的新数据的“合并”)。因此,如果您想确保每位用户都有姓名和年龄,可以使用:

{
  "rules": {
    "users": {
      "$user": {
        ".read": true,
        ".write": true,
        ".validate": "newData.hasChildren(['name', 'age'])"
      }
    }
  }
}

由于 newData 合并现有数据和新数据,因此即使是“部分”数据,它也能正常运行。更新。例如:

var fredRef = firebase.database().ref("users/fred");
// Valid since we have a name and age.
fredRef.set({ name: "Fred", age: 19 });
// Valid since we are updating the name but there's already an age.
fredRef.child("age").set(27);
// Invalid since the .validate rule will no longer be true.
fredRef.child("name").remove();

此 newData 变量在 .read 规则中不可用,因为没有写入新数据。您只能使用 data

RuleDataSnapshot:方法

从此 RuleDataSnapshot 中获取原始值(stringnumberbooleannull)。

返回值:(StringNumberBooleanNull) - 此 RuleDataSnapshot 中的原始值。

DataSnapshot.val() 不同,对具有子项数据的 RuleDataSnapshot 调用 val() 不会返回包含子项的对象。而是返回一个特殊的标记值。这可确保规则始终能非常高效地执行。

因此,您必须始终使用 child() 访问子项(例如,data.child('name').val(),而不是 data.val().name)。

此示例仅当所读取位置的 isReadable 子项设置为 true 时,才允许进行读取。

".read": "data.child('isReadable').val() == true"

child()

获取指定相对路径中位置的 RuleDataSnapshot

参数childPath String - 子数据所在位置的相对路径。

返回值RuleDataSnapshot - 子位置的 RuleDataSnapshot

相对路径可以是简单的子名称(例如“fred”),也可以是以斜杠分隔的路径(例如“fred/name/first”)。如果子位置没有任何数据,则返回空的 RuleDataSnapshot。

此示例仅当所读取位置的 isReadable 子项设置为 true 时,才允许进行读取。

".read": "data.child('isReadable').val() == true"

parent()

获取父位置的 RuleDataSnapshot

返回值RuleDataSnapshot - 父级位置的 RuleDataSnapshot

如果此实例引用了 Firebase Realtime Database 的根,则它没有父项,并且 parent() 将失败,导致当前规则表达式被跳过(作为失败)。

此示例只允许在 isReadable 同级项设置为 true 时进行读取。

".read": "data.parent().child('isReadable').val() == true"

hasChild(childPath)

如果指定的子元素存在,则返回 true。

参数childPath String - 潜在子项位置的相对路径。

返回值Boolean - true(如果指定子路径中存在数据);其他 false

此示例仅允许在数据包含子项“name”时写入数据。

".validate": "newData.hasChild('name')"

hasChildren([children])

检查是否存在子项。

参数children Array 可选 - 必须全部存在的子键数组。

返回值Boolean - true(如果指定)子项存在;否则 false

如果未提供任何参数,则当 RuleDataSnapshot 有任何子项时,它将返回 true。如果提供了子名称数组,则只有当所有指定的子项都存在于 RuleDataSnapshot 中时,该数组才会返回 true。

本示例仅允许在包含一个或多个子节点的数据时写入数据。

".validate": "newData.hasChildren()"

此示例仅允许写入包含“name”的数据和“age”子女。

".validate": "newData.hasChildren(['name', 'age'])"

已存在()

如果此 RuleDataSnapshot 包含任何数据,则返回 true。

返回值Boolean - true(如果 RuleDataSnapshot 包含任何数据);其他为 false

如果此 RuleDataSnapshot 包含任何数据,则现有的函数返回 true。这是一个纯粹的便捷函数,因为 data.exists() 等同于 data.val() != null

此示例允许在此位置执行写入操作,前提是没有现有数据。

".write": "!data.exists()"

getPriority()

获取 RuleDataSnapshot 中数据的优先级。

返回值:(StringNumberNull)- 此 RuleDataSnapshot 中数据的优先级。

此示例可确保正在写入的新数据的优先级

".validate": "newData.getPriority() != null"

isNumber()

如果此 RuleDataSnapshot 包含数值,则返回 true。

返回值Boolean - true(如果数据为数字);否则 false

此示例可确保正在写入的新数据具有子项“age”以及一个数值。

".validate": "newData.child('age').isNumber()"

isString()

如果此 RuleDataSnapshot 包含字符串值,则返回 true。

返回值Boolean - true(如果数据为 String);其他 false

此示例确保正在写入的新数据具有子项“name”以及一个字符串值。

".validate": "newData.child('name').isString()

isBoolean()

如果此 RuleDataSnapshot 包含布尔值,则返回 true。

返回值Boolean - true(如果数据为 Boolean);否则 false

此示例可确保正在写入的新数据具有“活动”子项以及布尔值。

".validate": "newData.child('active').isBoolean()"

字符串:属性

长度

返回字符串的长度。

返回值Number - 字符串中的字符数。

此示例要求字符串至少包含 10 个字符。

".validate": "newData.isString() && newData.val().length >= 10"

字符串:方法

包含(子字符串)

如果字符串包含指定的子字符串,则返回 true。

参数substring String - 要查找的子字符串。

返回值Boolean - true(如果该字符串包含指定的子字符串);否则 false

此示例要求数据必须是包含“@”的字符串。

".validate": "newData.isString() && newData.val().contains('@')"

StartWith(子字符串)

如果字符串以指定子字符串开头,则返回 true。

参数substring String - 要在开头查找的子字符串。

返回值Boolean - true(如果该字符串包含指定的子字符串);否则 false

此示例允许在 auth.token.identifier 以“internal-”开头时授予读取权限

".read": "auth.token.identifier.beginsWith('internal-')"

EndWith(子字符串)

如果字符串以指定子字符串结尾,则返回 true。

参数substring String - 要在末尾查找的子字符串。

返回值Boolean - true(如果该字符串以指定子字符串结尾);否则 false

此示例允许对以“@company.com”结尾的 auth.token.identifier 拥有读取权限

".read": "auth.token.identifier.endsWith('@company.com')"

替换为(子字符串,替换)

返回字符串的副本,并将指定子字符串的所有实例替换为指定的替换字符串。

参数substring String - 要查找的子字符串。 replacement String - 用于替换子字符串的字符串。

返回值String - 用替换项替换子字符串后的新字符串。

replace() 方法与 JavaScript replace() 方法略有不同,因为它会将指定子字符串的所有实例替换为指定的替换字符串,而不仅仅是第一个实例。

由于键中不允许使用句点,因此我们需要先使用句点转义字符串,然后再存储字符串。电子邮件地址便是一个这样的例子。假设我们的 /whitelist/ 节点中有一个已列入白名单的电子邮件地址列表:

{
 "user": {
   "$uid": {
     "email": <email>
   }
 },
 "whitelist": {
   "fred@gmail%2Ecom": true,
   "barney@aol%2Ecom": true
 }
}

我们可以制定一条规则,仅允许用户加入其电子邮件地址在 /whitelist/ 节点中的情况:

{
  "rules": {
    "users": {
      "$uid": {
        ".read": "true",
        ".write": "root.child('whitelist').child(newData.child('email').val().replace('.', '%2E')).exists()"
      }
    }
  }
}

toLowerCase()

返回转换为小写的字符串的副本。

返回值String - 转换为小写的字符串。

如果 auth.token.identifier 下存在所有小写字符,则此示例允许读取权限。/users

".read": "root.child('users').child(auth.token.identifier.toLowerCase()).exists()"

toUpperCase()

返回转换为大写的字符串的副本。

返回值String - 转换为大写的字符串。

此示例允许 auth.token.identifier 进行读取访问,因为 /users 下全部大写。

".read": "root.child('users').child(auth.token.identifier.toUpperCase()).exists()"

匹配(正则表达式)

如果字符串与指定的正则表达式字面量匹配,则返回 true。

返回值Boolean - 如果字符串与正则表达式字面量 regex 匹配,则为 true;否则 false

查看完整的规则正则表达式文档

运算符

+(添加)

用于添加变量或字符串串联。

以下示例可以确保新值将现有值正好增加 1。这对于实现计数器非常有用:

".write": "newData.val() === data.val() + 1"
".validate": "root.child('room_names/' + $room_id).exists()"

-(否定或减)

用于否定规则表达式中的一个值或减去两个值。

此验证规则检查新值是否为该位置处的子值的反函数:

".validate": "newData.val() === -(data.child('quantity').val())"

以下示例使用减法来确保只能读取过去十分钟内的消息:

".read": "newData.child('timestamp').val() > (now - 600000)"

*(乘号)

用于相乘规则表达式中的变量。

此验证规则会检查新值是否与价格和数量(两个现有值)的产物相等:

".validate": "newData.val() === data.child('price').val() * data.child('quantity').val()"

/(除号)

用于划分规则表达式中的变量。

在以下示例中,验证规则会确保存储的数据是存储在其他位置的总数据的平均值:

".validate": "newData.val() === data.parent().child('sum').val() / data.parent().child('numItems').val()"

%(模数)

用于查找规则表达式中一个变量除以另一个变量的余数。

此规则用于验证是否只能写入偶数:

".validate": "newData.val() % 2 === 0"

===(等于)

用于检查规则表达式中的两个变量是否具有相同的类型和值。

以下规则使用 === 运算符,仅向用户账号的所有者授予写入权限。用户的 uid 必须与键 ($user_id) 完全匹配,才能使规则评估为 true。

"users": {
  ".write": "$user_id === auth.uid"
}

!==(不等于)

用于检查规则表达式中两个变量是否不相等。

以下读取规则可确保只有已登录的用户才能读取数据:

".read": "auth !== null"

&&(且)

如果两个操作数都为 true,则求值结果为 true。用于对规则表达式中的多个条件求值。

以下验证规则会检查新数据是否为少于 100 个字符的字符串:

".validate": "newData.isString() && newData.val().length < 100"

||(或)

如果规则表达式中的一个操作数为 true,则求值结果为 true。

在此示例中,只要旧数据或新数据不存在,我们就可以写入。换言之,如果我们要删除或创建数据,但没有更新数据,我们可以写入数据。

".write": "!data.exists() || !newData.exists()"

!(非)

如果单个操作数为 false,则求值结果为 true。在规则表达式中,!运算符通常用于查看数据是否已写入某个位置。

以下规则仅当指定位置没有任何数据时允许写入权限:

".write": "!data.exists()"

>(大于)

用于检查某个值是否大于规则表达式中的另一个值。

此验证规则可检查要写入的字符串是否不为空字符串:

".validate": "newData.isString() && newData.val().length > 0"

<(小于)

用于检查某个值是否小于规则表达式中的另一个值。

以下验证规则会检查字符串是否少于 20 个字符:

".validate": "newData.isString() && newData.val().length < 20"

>=(大于或等于)

用于检查某个值是否大于或等于规则表达式中的另一个值。

此验证规则可检查要写入的字符串是否不为空字符串:

".validate": "newData.isString() && newData.val().length >= 1"

<=(小于或等于)

用于检查某个值是否小于或等于规则表达式中的另一个值。

此验证规则可确保以后不会添加新数据:

".validate": "newData.val() <= now"

?(三元运算符)

用于对条件规则表达式求值。

三元运算符接受三个运算数。? 前面的操作数,是条件。如果条件计算结果为 true,则评估第二个操作数。如果条件为 false,则对第三个操作数进行求值。

对于以下验证规则,新值可以是数字或布尔值。如果为数字,则必须大于 0。

".validate": "newData.isNumber() ? newData.val() > 0 : newData.isBoolean()"