Firebase 数据库安全规则 API

规则:类型

。读

授予客户端对 Firebase 实时数据库位置的读取权限。

.read规则是一种安全规则,它授予客户端对 Firebase 实时数据库位置的读取访问权限。例如:

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

.read规则的值是一个字符串,它被评估为 JavaScript 表达式语法的子集,并进行了一些行为更改以提高清晰度和正确性。授予读取位置权限的.read规则也将允许读取该位置的任何后代,即使后代有自己的失败的.read规则。

.read规则可以访问除newData之外的所有 Firebase 实时数据库规则变量

。写

授予客户端对 Firebase 实时数据库位置的写入权限。

.write规则是一种安全规则,它授予客户端对 Firebase 实时数据库位置的写入权限。例如:

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

.write规则的值是一个字符串,它被评估为 JavaScript 表达式语法的子集,并进行了一些行为更改以提高清晰度和正确性。授予写入位置权限的.write规则也将允许写入该位置的任何后代,即使后代有自己的失败的.write规则。

.write规则可以访问 Firebase 实时数据库的所有规则变量

。证实

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

一旦.write规则授予访问权限,就会使用.validate规则,以确保写入的数据符合特定标准。除了授予访问权限的.write之外,所有相关的.validate规则都必须成功才能允许写入。例如:

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

.validate规则的值是一个字符串,它作为 JavaScript 表达式语法的子集进行计算,并进行一些行为更改以提高清晰度和正确性。

.validate规则可以访问 Firebase 实时数据库的所有规则变量

.indexOn

通过告诉 Firebase 实时数据库您希望为数据索引哪些键来提高查询性能。

.indexOn规则告诉 Firebase 实时数据库服务器对数据中的特定键建立索引,以提高查询性能。例如,给定一个包含恐龙数据集合的数据库,我们可以通过添加以下规则告诉 Firebase 实时数据库在从服务器返回查询之前对查询进行优化:

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

您可以通过参考安全指南中有关为数据建立索引的部分来了解有关.indexOn规则的更多信息。

规则:变量

授权

如果客户端已通过身份验证,则包含令牌有效负载的变量;如果客户端未通过身份验证,则包含null

Firebase 实时数据库允许您轻松地向多个内置提供商进行身份验证,并为它们生成身份验证令牌。用户通过内置提供程序之一进行身份验证后,auth 变量将包含以下内容:

场地描述
provider使用的身份验证方法(例如“密码”、“匿名”、“facebook”、“github”、“google”或“twitter”)。
uid唯一的用户 ID,保证在所有提供商中都是唯一的。
token Firebase 身份验证 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'"
    }
  }
}

授权令牌

包含 Firebase 身份验证 ID 令牌内容的变量。

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

场地描述
email与帐户关联的电子邮件地址(如果存在)。
email_verified如果用户已验证他们有权访问email地址,则为true 。一些提供商会自动验证他们拥有的电子邮件地址。
phone_number与帐户关联的电话号码(如果存在)。
name用户的显示名称(如果已设置)。
sub用户的 Firebase 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与帐户关联的tenantId(如果存在)。例如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时,您可以在规则表达式中使用匹配的$变量来获取正在读取或写入的实际子项的名称。因此,假设我们希望为每个用户提供对其自己的/users/<user>位置的读写访问权限。我们可以使用:

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

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

现在

根据 Firebase 实时数据库服务器,包含自 Unix 纪元以来的毫秒数。

now变量包含根据 Firebase 实时数据库服务器的 UNIX 纪元以来的毫秒数。例如,您可以使用它来验证用户的created时间从未设置为将来的时间:

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

与 Firebase 实时数据库根目录中的当前数据相对应的RuleDataSnapshot

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

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

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

数据

与 Firebase 实时数据库中当前执行规则位置的当前数据相对应的RuleDataSnapshot

data 变量为您提供与当前执行规则的数据库位置中的当前数据相对应的RuleDataSnapshot (与 root 不同,root 为您提供数据库根的数据)。

例如,如果您想让任何客户端访问/users/<user> (如果/users/<user>/public设置为 true),您可以使用:

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

data 变量在.read.write.validate规则中可用。

新数据

与允许写入时生成的数据相对应的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规则中不可用,因为没有写入新数据。你应该只使用数据

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"

孩子()

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

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

返回值RuleDataSnapshot - 子位置的RuleDataSnapshot

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

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

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

父级()

获取父位置的RuleDataSnapshot

返回值RuleDataSnapshot - 父位置的RuleDataSnapshot

如果此实例引用 Firebase 实时数据库的根,则它没有父级, parent()将失败,导致当前规则表达式被跳过(作为失败)。

仅当 isReadable 同级设置为 true 时,此示例才允许读取。

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

hasChild(子路径)

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

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

返回值Boolean - 如果数据存在于指定的子路径中, true ;否则false

此示例仅允许写入包含子“名称”的数据。

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

有孩子([孩子])

检查儿童是否存在。

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

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

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

此示例仅允许写入包含一个或多个子项的数据。

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

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

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

存在()

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

返回值Boolean - 如果RuleDataSnapshot包含任何数据, true ;否则false

如果此RuleDataSnapshot包含任何数据,则exists 函数将返回true。它纯粹是一个便利函数,因为data.exists()相当于data.val() != null

只要没有现有数据,此示例就允许在此位置进行写入。

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

获取优先级()

获取RuleDataSnapshot中数据的优先级。

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

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

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

isNumber()

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

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

此示例确保写入的新数据具有带有数值的子“age”。

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

isString()

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

返回值Boolean - 如果数据是String true ;否则false

此示例确保写入的新数据具有带有字符串值的子“name”。

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

是布尔值()

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

返回值Boolean - 如果数据是Booleantrue ;否则false

此示例确保写入的新数据具有带有布尔值的子“active”。

".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('@')"

开始于(子字符串)

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

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

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

如果auth.token.identifier以“internal-”开头,则此示例允许读取访问

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

以(子字符串)结尾

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

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

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

如果auth.token.identifier以“@company.com”结尾,则此示例允许读取访问

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

替换(子字符串,替换)

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

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

返回值String - 用 replacement 替换 substring 后的新字符串。

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 - 转换为小写的字符串。

如果/users下存在全小写的auth.token.identifier ,则此示例允许读取访问。

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

toUpperCase()

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

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

如果/users下存在全部大写的auth.token.identifier ,则此示例允许读取访问。

".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,则计算第二个操作数。如果条件为假,则计算第三个操作数。

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

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