Firebase实时数据库安全规则是您如何保护数据免遭未经授权的用户使用和保护数据结构的方法。
在此快速提示教程中,我将说明如何正确配置数据库安全规则,以便只有授权用户才能读取或写入数据。 我还将向您展示如何构造数据以使其易于保护。
问题
假设我们的Firebase数据库中有JSON数据,如下例所示:
{
"users" : {
"user1" : {
"firstName" : "Chike",
"lastName" : "Mgbemena",
"age": "89"
"phoneNumber" : "07012345678"
},
"user2" : {
"firstName" : "Godswill",
"lastName" : "Okwara",
"age": "12"
"phoneNumber" : "0701234"
},
"user3" : {
"firstName" : "Onu",
"lastName" : 543,
"age": 90
"phoneNumber" : "07012345678"
},
...
}
}
查看数据库,您会发现我们的数据存在一些问题:
- 两个用户(
user1
和user3
)具有相同的电话号码。 我们希望这些独特。
-
user3
有一个姓氏数字,而不是字符串。 -
user2
的电话号码中只有7位数字,而不是11位数字。 -
user1
和user2
的年龄值是一个字符串,而user3
的年龄值是一个数字。
由于我们的数据突出显示了所有这些缺陷,因此我们失去了数据完整性。 在以下步骤中,我将向您展示如何防止这些情况的发生。
允许规则
Firebase实时数据库具有以下规则类型:
类型 | 功能 |
---|---|
.read | 描述是否以及何时允许用户读取数据。 |
.write | 描述是否以及何时允许写入数据。 |
.validate | 定义格式正确的值的外观,是否具有子属性以及数据类型。 |
.indexOn | 指定要索引的子级以支持排序和查询。 |
在Firebase文档中阅读有关它们的更多信息。
对于数据库中的users
密钥,这是一个非常宽松的规则。
{
"rules": {
"users": {
// users is readable by anyone
".read": true,
// users is writable by anyone
".write": true
}
}
}
这很不好,因为它使任何人都可以读取或写入数据库数据。 任何人都可以访问/users/
路径以及更深的路径。 不仅如此,而且用户的数据没有结构。
访问控制规则
{
"rules": {
"users": {
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid",
}
}
}
}
使用这些规则,我们可以控制对登录用户的用户记录的访问。 不仅如此,用户只能读取或写入自己的数据。 我们使用通配符$uid
。 这是代表子项的变量(变量名以$
开头)。 例如,访问路径/users/user1
, $uid
是"user1"
。
接下来,我们使用auth
变量,该变量代表当前已通过身份验证的用户。 这是Firebase提供的预定义服务器变量。 在第5和第6行中,我们实施了可访问性约束,即只有与用户记录具有相同ID的经过身份验证的用户才能读取或写入其数据。 换句话说,对于每个用户,对/users/<uid>/
授予读写访问权限,其中<uid>
代表当前已认证的用户ID。
其他Firebase服务器变量是:
now | 自Linux时代以来的当前时间(以毫秒为单位)。 |
root | 一个RuleDataSnapshot 表示Firebase数据库中尝试执行操作之前存在的根路径。 |
newData | RuleDataSnapshot 表示尝试操作后将存在的数据。 它包括正在写入的新数据和现有数据。 |
data | RuleDataSnapshot表示尝试操作之前存在的数据。 |
auth | 表示经过身份验证的用户的令牌有效负载。 |
在Firebase文档中阅读有关这些和其他服务器变量的更多信息。
实施数据结构
我们还可以使用Firebase规则对数据库中的数据实施约束。
例如,在以下规则的第8行和第11行中,我们确保规则确保名字和姓氏的任何新值都必须是字符串。 在第14行中,我们确保年龄是一个数字。 最后,在第17和18行中,我们强制电话号码值必须是字符串且长度为11。
{
"rules": {
"users": {
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid",
"firstName": {
".validate": "newData.isString()"
},
"lastName": {
".validate": "newData.isString()"
},
"age": {
".validate": "newData.isNumber()"
},
"phoneNumber": {
".validate": "newData.isString() &&
newData.val().length == 11"
},
}
}
}
}
但是,如何防止重复的电话号码?
防止重复
接下来,我将向您展示如何防止电话号码重复。
步骤1:标准化数据结构
我们需要做的第一件事是修改根路径,使其包含顶级/phoneNumbers/
节点。 因此,在创建新用户时,验证成功后,我们还将用户的电话号码添加到该节点。 我们的新数据结构如下所示:
{
"users" : {
"user1" : {
"firstName" : "Chike",
"lastName" : "Mgbemena",
"age": 89,
"phoneNumber" : "07012345678"
},
"user2" : {
"firstName" : "Godswill",
"lastName" : "Okwara",
"age": 12,
"phoneNumber" : "06034345453"
},
"user3" : {
"firstName" : "Onu",
"lastName" : "Emeka",
"age": 90,
"phoneNumber" : "09034564543"
},
...
},
"phoneNumbers" : {
"07012345678": "user1",
"06034345453": "user2",
"09034564543": "user3",
...
}
}
步骤2:实施新的数据结构
我们需要修改安全规则以强制执行数据结构:
{
"rules": {
"users": {
"$uid": {
...
"phoneNumber": {
".validate": "newData.isString() &&
newData.val().length == 11 &&
!root.child('phoneNumbers').child(newData.val()).exists()"
},
}
}
}
}
在这里,我们通过检查电话号码是否已经是/phoneNumbers/
节点的子级(以给定电话号码作为键)来确保其唯一性。 换句话说,我们正在检查用户尚未注册电话号码。 如果还没有,则验证成功,并且写入操作将被接受,否则将被拒绝。
创建新用户时,您的应用需要将电话号码添加到电话号码列表中,并且如果删除该用户,则需要删除该用户的电话号码。
模拟验证和安全规则
您可以通过单击“ 模拟器”按钮在Firebase控制台中模拟安全规则。 添加您的安全规则,选择模拟类型(读或写),使用路径输入一些数据,然后单击“ 运行”按钮:
如果名字的值是数字而不是字符串,则验证将失败并且写入访问被拒绝:
结论
在此快速提示教程中,您了解了Firebase数据库安全规则:如何防止对数据的未经授权的访问以及如何确保数据库中数据的结构化。
翻译自: https://code.tutsplus.com/articles/quick-tip-firebase-database-security-rules--cms-27926