限制通过安全规则中的某个子值进行查询
Restrict querying by a certain child value in security rules
我正在努力想出构建部分数据库及其相关安全规则的最佳方法。
我有聊天组,用户可以随时添加到这些组中。将用户添加到组后,他们应该只能检索在 之后发送的消息。他们应该不可能检索在他们(用户)被添加到组之前发送的任何消息。
我的第一种方法错误地假设安全规则只适用于被查询的数据。
为了简化这个问题,我有以下结构:
{
"groups": {
"-Kb9fw20GqapLm_b8JNE": {
"name": "Cool people"
}
},
"groupUsers": {
"-Kb9fw20GqapLm_b8JNE": {
"3JzxHLv4b6TcUBvFL64Tyt8dTXJ2": {
"timeAdded": 1230779183745
},
"S2GMKFPOhVhzZL7q4xAVFIHTmRC3": {
"timeAdded": 1480113719485
}
}
},
"groupMessages": {
"-Kb9fw20GqapLm_b8JNE": {
"-KbKWHv4J4XN22aLMzVa": {
"from": "3JzxHLv4b6TcUBvFL64Tyt8dTXJ2",
"text": "Hello",
"timeSent": "1358491277463"
},
"-KfHxtwef6_S9C5huGLI": {
"from": "S2GMKFPOhVhzZL7q4xAVFIHTmRC3",
"text": "Goodbye",
"timeSent": "1493948817230"
}
}
}
}
这些安全规则:
{
"rules": {
"groupMessages": {
".indexOn": "timeSent",
"$groupKey": {
".read": "root.child('groupUsers').child(auth.uid).child($groupKey).child('timeAdded').val() <= data.child('timeSent').val()"
".write": "!data.exists() && root.child('groupUsers').child(auth.uid).child($groupKey).exists() && newData.child('from').val() === auth.uid",
}
}
}
}
有了这个,我想我可以像这样检索特定组的消息:
var myTimeAdded = /* already retrieved from the database */;
firebase.database()
.ref('groupMessages/-Kb9fw20GqapLm_b8JNE')
.orderByChild('timeSent')
.startAt(myTimeAdded)
.on('child_added', /* ... */);
但正如我所说,这是一个错误的假设。关于我如何实现这一点有什么建议吗?
读取规则在您附加侦听器的位置强制执行。
所以在你的情况下是 groupMessages/-Kb9fw20GqapLm_b8JNE
。如果您的用户具有读取权限,则允许侦听器。如果用户没有读取权限,监听器是rejected/cancelled.
这意味着不能使用规则来过滤数据。我们经常将其称为 "rules are not filters",对于刚接触 Firebase 安全模型的开发人员来说,这是最常见的陷阱之一。参见:
- Firebase 文档中的 rules are not filters 部分
- 上一个questions about Firebase that mention "rules are not filters"
您的规则本身并没有错:它们只允许访问每个特定的 child,前提是它不太旧。他们只是不允许您再 运行 查询 groupMessages/-Kb9fw20GqapLm_b8JNE
。
解决此问题的常用方法是使用一个单独的结构(通常称为 "index"),其中包含 项目的键,否则您的查询将 return。在你的情况下,它看起来可能会变成每个用户在加入后使用所有消息的键的索引。
但老实说,听起来您正试图在此处以 SQL 方式使用安全规则。似乎不太可能 不允许 用户查看旧消息。更有可能的是您不希望用户被旧消息打扰。在那种情况下,我只是通过查询解决它(正如您已经拥有的那样)并删除 ".read"
规则。
我正在努力想出构建部分数据库及其相关安全规则的最佳方法。
我有聊天组,用户可以随时添加到这些组中。将用户添加到组后,他们应该只能检索在 之后发送的消息。他们应该不可能检索在他们(用户)被添加到组之前发送的任何消息。
我的第一种方法错误地假设安全规则只适用于被查询的数据。
为了简化这个问题,我有以下结构:
{
"groups": {
"-Kb9fw20GqapLm_b8JNE": {
"name": "Cool people"
}
},
"groupUsers": {
"-Kb9fw20GqapLm_b8JNE": {
"3JzxHLv4b6TcUBvFL64Tyt8dTXJ2": {
"timeAdded": 1230779183745
},
"S2GMKFPOhVhzZL7q4xAVFIHTmRC3": {
"timeAdded": 1480113719485
}
}
},
"groupMessages": {
"-Kb9fw20GqapLm_b8JNE": {
"-KbKWHv4J4XN22aLMzVa": {
"from": "3JzxHLv4b6TcUBvFL64Tyt8dTXJ2",
"text": "Hello",
"timeSent": "1358491277463"
},
"-KfHxtwef6_S9C5huGLI": {
"from": "S2GMKFPOhVhzZL7q4xAVFIHTmRC3",
"text": "Goodbye",
"timeSent": "1493948817230"
}
}
}
}
这些安全规则:
{
"rules": {
"groupMessages": {
".indexOn": "timeSent",
"$groupKey": {
".read": "root.child('groupUsers').child(auth.uid).child($groupKey).child('timeAdded').val() <= data.child('timeSent').val()"
".write": "!data.exists() && root.child('groupUsers').child(auth.uid).child($groupKey).exists() && newData.child('from').val() === auth.uid",
}
}
}
}
有了这个,我想我可以像这样检索特定组的消息:
var myTimeAdded = /* already retrieved from the database */;
firebase.database()
.ref('groupMessages/-Kb9fw20GqapLm_b8JNE')
.orderByChild('timeSent')
.startAt(myTimeAdded)
.on('child_added', /* ... */);
但正如我所说,这是一个错误的假设。关于我如何实现这一点有什么建议吗?
读取规则在您附加侦听器的位置强制执行。
所以在你的情况下是 groupMessages/-Kb9fw20GqapLm_b8JNE
。如果您的用户具有读取权限,则允许侦听器。如果用户没有读取权限,监听器是rejected/cancelled.
这意味着不能使用规则来过滤数据。我们经常将其称为 "rules are not filters",对于刚接触 Firebase 安全模型的开发人员来说,这是最常见的陷阱之一。参见:
- Firebase 文档中的 rules are not filters 部分
- 上一个questions about Firebase that mention "rules are not filters"
您的规则本身并没有错:它们只允许访问每个特定的 child,前提是它不太旧。他们只是不允许您再 运行 查询 groupMessages/-Kb9fw20GqapLm_b8JNE
。
解决此问题的常用方法是使用一个单独的结构(通常称为 "index"),其中包含 项目的键,否则您的查询将 return。在你的情况下,它看起来可能会变成每个用户在加入后使用所有消息的键的索引。
但老实说,听起来您正试图在此处以 SQL 方式使用安全规则。似乎不太可能 不允许 用户查看旧消息。更有可能的是您不希望用户被旧消息打扰。在那种情况下,我只是通过查询解决它(正如您已经拥有的那样)并删除 ".read"
规则。