我们提供的服务,人们将嵌入到他们的网站,我们希望使用Firebase作为我们的后端。我们希望我们的订阅率的网页浏览或类似的基础。现在,我们很难理解如何防止客户缓存客户端js代码,而忽略了试图增加页面视图计数器的任何部分。
我们需要做的是以某种方式创建一个安全规则,该规则原子地阻止某人从一个位置读取数据,除非他们在另一个位置增加了计数器。对怎么做有什么想法吗?
例如,假设以下模式:
{
"comments" : {
"-JYlV8KQGkUk18-nnyHk" : {
"content" : "This is the first comment."
},
"-JYlV8KWNlFZHLbOphFO" : {
"content" : "This is a reply to the first.",
"replyToCommentId" : "-JYlV8KQGkUk18-nnyHk"
},
"-JYlV8KbT63wL9Sb0QvT" : {
"content" : "This is a reply to the second.",
"replyToCommentId" : "-JYlV8KWNlFZHLbOphFO"
},
"-JYlV8KelTmBr7uRK08y" : {
"content" : "This is another reply to the first.",
"replyToCommentId" : "-JYlV8KQGkUk18-nnyHk"
}
},
oldPageViews: 32498,
pageViews: 32498
}如果客户端首先增加pageViews字段,那么只允许读取注释的方式是什么?起初,我想要有两个字段(比如pageViews和oldPageViews),从增量pageViews开始,读取注释,然后递增oldPageViews以匹配,并且只允许在pageViews === oldPageViews + 1中读取注释。然而,除非可以原子地完成,否则如果客户端启动进程但没有完成,数据可能会进入损坏状态。
这里是一个试图测试这个想法的代码爱好者。
发布于 2014-10-25 14:56:56
我建议改变加藤的限速答案:https://stackoverflow.com/a/24841859/75644
数据:
{
"comments": {
"-JYlV8KQGkUk18-nnyHk": {
"content": "This is the first comment."
},
"-JYlV8KWNlFZHLbOphFO": {
"content": "This is a reply to the first.",
"replyToCommentId": "-JYlV8KQGkUk18-nnyHk"
},
"-JYlV8KbT63wL9Sb0QvT": {
"content": "This is a reply to the second.",
"replyToCommentId": "-JYlV8KWNlFZHLbOphFO"
},
"-JYlV8KelTmBr7uRK08y": {
"content": "This is another reply to the first.",
"replyToCommentId": "-JYlV8KQGkUk18-nnyHk"
},
"timestamp" : 1413555509137
},
"pageViews" : {
"count" : 345030,
"lastTs" : 1413555509137
}
}安全规则:
{
"rules": {
"pageViews": {
".validate": "newData.hasChildren(['count','lastTs'])",
"count": {
".validate": "newData.exists() && newData.isNumber() && newData.val() > data.val()"
},
"lastTs": {
// timestamp can't be deleted or I could just recreate it to bypass our throttle
".write": "newData.exists()",
// the new value must be at least 500 milliseconds after the last (no more than one message every five seconds)
// the new value must be before now (it will be since `now` is when it reaches the server unless I try to cheat)
".validate": "newData.isNumber() && newData.val() === now && (!data.exists() || newData.val() > data.val()+500)"
}
},
"comments": {
// The comments can't be read unless the pageViews lastTs value is within 500 milliseconds of now
".read": "root.child('pageViews').child('lastTs').val() > now - 501",
".write": true
}
}
}注意:我还没有测试过这个,所以您需要使用它来看看它是否有效。
另外,根据您的示例数据,我没有处理uid的数据,您需要确保您正在管理谁可以在这里读写。
发布于 2014-10-27 20:06:09
Justin对节流代码的适应似乎是一个很好的起点。还有一些令人讨厌的漏洞,比如强制更新计数器,从计数器中获取可量化的度量/分析(这需要通过某种方式连接到一个统计工具,并且对于准确的账单报告和客户查询也是必要的),并且能够准确地确定访问何时“结束”。
根据Justin最初的想法,我认为通过简化客户所负责的金额,可以省去大量的开销。也许是这样:
从这个基础开始,我将调整安全规则和结构如下:
{
"rules": {
"count": {
// updated only from node.js script
// assumes our node worker authenticates with a special uid we created
// http://jsfiddle.net/firebase/XDXu5/embedded/result/
".write": "auth.uid === 'ADMIN_WORKER'",
".validate": "newData.exists() && newData.isNumber() && newData.val() > data.val()"
},
"lastTs": {
// timestamp can't be deleted or I could just recreate it to bypass our throttle
".write": "newData.exists()",
// the new value must be equal to now (i.e. Firebase.ServerValue.TIMESTAMP)
".validate": "newData.isNumber() && newData.val() === now"
},
"comments": {
// The comments can't be read unless the pageViews lastTs value is within 30 seconds
".read": "root.child('pageViews').child('lastTs').val() > now - 30000",
"$comment": {
".write": "???"
}
}
}
}现在,我将编写一个简单的节点脚本来执行计数和管理任务:
var Firebase = require('firebase');
var ref = new Firebase(URL);
ref.child('lastTs').on('value', heartbeatReceived);
var lastCheck = null;
function heartbeatReceived(snap) {
if( isNewSession(snap.val()) ) {
incrementCounter();
}
updateStatsEngine(snap);
}
function incrementCounter() {
ref.child('count').transaction(function(currVal) {
return (currVal||0) + 1;
});
}
function isNewSession(timestamp) {
// the criteria here is pretty arbitrary and up to you, maybe
// something like < 30 minutes since last update or the same day?
var res = lastCheck === null || timestamp - lastCheck > 30 * 60 * 1000;
lastCheck = timestamp;
return res;
}
function updateStatsEngine(snap) {
// contact keen.io via their REST API
// tell intercom.io that we have an event
// do whatever is desired to store quantifiable stats
// and track billing info
//
//var client = require('keen.io').configure({
// projectId: "<project_id>",
// writeKey: "<write_key>",
// readKey: "<read_key>",
// masterKey: "<master_key>"
//});
//
//client.addEvent("collection", {/* data */});
}这种方法的缺点是,如果我的管理脚本出现故障,那么这段时间内的任何事件都不会被记录。然而,这个脚本的美妙之处在于它的简单。
它不会有太多的bug。添加monit、upstart或其他工具,以确保它不会崩溃。工作完成了。
它也有很高的用途。我可以在我的笔记本电脑,甚至我的Android手机(作为HTML页面)在紧要关头运行它。
https://stackoverflow.com/questions/26520793
复制相似问题