假设我的 Firebase 集合如下所示:
{
"max":5
"things":{}
}
我将如何使用max
安全规则中的值来限制 的数量things
?
{
"rules": {
"things": {
".validate": "newData.val().length <= max"
}
}
}
假设我的 Firebase 集合如下所示:
{
"max":5
"things":{}
}
我将如何使用max
安全规则中的值来限制 的数量things
?
{
"rules": {
"things": {
".validate": "newData.val().length <= max"
}
}
}
使用现有属性是通过root或parent 完成的,非常简单。
{
"rules": {
"things": {
// assuming value is being stored as an integer
".validate": "newData.val() <= root.child('max')"
}
}
}
但是,确定记录数量并执行此操作比简单地编写安全规则要复杂一些:
.length
对象上没有,我们需要存储存在多少条记录一种天真的方法
假设限制很小(例如 5 条记录),一个穷人的方法是简单地在安全规则中枚举它们:
{
"rules": {
"things": {
".write": "newData.hasChildren()", // is an object
"thing1": { ".validate": true },
"thing2": { ".validate": true },
"thing3": { ".validate": true },
"thing4": { ".validate": true },
"thing5": { ".validate": true },
"$other": { ".validate": false
}
}
}
一个真实的例子
像这样的数据结构有效:
/max/<number>
/things_counter/<number>
/things/$record_id/{...data...}
因此,每次添加记录时,计数器必须递增。
var fb = new Firebase(URL);
fb.child('thing_counter').transaction(function(curr) {
// security rules will fail this if it exceeds max
// we could also compare to max here and return undefined to cancel the trxn
return (curr||0)+1;
}, function(err, success, snap) {
// if the counter updates successfully, then write the record
if( err ) { throw err; }
else if( success ) {
var ref = fb.child('things').push({hello: 'world'}, function(err) {
if( err ) { throw err; }
console.log('created '+ref.name());
});
}
});
每次删除记录时,计数器必须递减。
var recordId = 'thing123';
var fb = new Firebase(URL);
fb.child('thing_counter').transaction(function(curr) {
if( curr === 0 ) { return undefined; } // cancel if no records exist
return (curr||0)-1;
}, function(err, success, snap) {
// if the counter updates successfully, then write the record
if( err ) { throw err; }
else if( success ) {
var ref = fb.child('things/'+recordId).remove(function(err) {
if( err ) { throw err; }
console.log('removed '+recordId);
});
}
});
现在进入安全规则:
{
"rules": {
"max": { ".write": false },
"thing_counter": {
".write": "newData.exists()", // no deletes
".validate": "newData.isNumber() && newData.val() >= 0 && newData.val() <= root.child('max').val()"
},
"things": {
".write": "root.child('thing_counter').val() < root.child('max').val()"
}
}
}
请注意,这不会强制用户在更新记录之前写入 thing_counter,因此虽然适合限制记录数量,但不适合执行游戏规则或防止作弊。
其他资源和想法
如果您需要游戏级别的安全性,请查看此 fiddle,其中详细介绍了如何使用增量 ID 创建记录,包括强制执行计数器所需的安全规则。您可以将其与上述规则结合起来,对增量 id 强制执行最大值,并确保在写入记录之前更新计数器。
此外,请确保您没有过度考虑这一点,并且有一个合法的用例来限制记录数量,而不仅仅是为了满足健康的担忧。简单地在您的数据结构上强制执行穷人的配额是非常复杂的。
虽然我认为仍然没有可用的规则来做这样的事情,但这里有一个示例云函数可以做到这一点:
https://github.com/firebase/functions-samples/tree/master/limit-children