Jar*_*nal 12 firebase firebase-security firebase-realtime-database
我正在使用Firebase开发多人游戏.每场比赛后,玩家得分记录在火力基础中,并且玩家总得分字段也会使用新总数进行更新.我的问题:是否有可能使用firebase安全规则来保护playerTotalScore字段免受用户的任意操纵?如果是这样,怎么样?
我已详细阅读了firebase网站上的firebase安全信息.虽然我理解可以在安全规则中实现一些复杂的逻辑(按照给定的数量增加一个数字,例如这个要点,或者只使用字段插入(".write": "!data.exists()"),但在这种情况下,没有任何信息似乎有帮助. - 仅仅规则是不够的,因为分数可以通过多次递增来操纵.仅插入似乎是totalScore的一个选项,因为它在每次游戏后更新.
根据加藤的要求,这是具体的用例.
我正在开发的游戏是一个问答游戏,其中玩家回答问题,并且玩家得分实时显示.
在游戏过程中,通过以下声明在每个问题之后更新该特定游戏的得分:
gameRef.child('players').child(UserId).child('score').set(gameScore)
Run Code Online (Sandbox Code Playgroud)
游戏结束后,totalScore=totalScore+gameScore玩家的总分数(所有游戏玩法)计算为,然后使用以下语句在Firebase中更新玩家总分:
leaderboardRef.child(userId).setWithPriority({userName:userName, totalScore:totalScore}, totalScore)
Run Code Online (Sandbox Code Playgroud)
这是我目前具体的具体结构.这不是一成不变的,所以我愿意根据建议的方法来保护数据,无论如何需要更改它.
用户(玩家)所玩的每个游戏的得分存储在以下结构中
<firebase_root>/app/games/<gameId>/players/<userId>/score/
Run Code Online (Sandbox Code Playgroud)
<gameId>是firebase生成的密钥,作为调用firebase push()方法的结果.
<UserId>是firebase simplelogin uid.
每个用户(玩家)的总分数(所有游戏的所有分数的总和)存储在以下数据结构中
<firebase_root>/app/leaderboard/<userId>/totalScore/
Run Code Online (Sandbox Code Playgroud)
出于查询目的,总分数的排行榜数据使用totalScore作为优先级进行设置
leaderboardRef.child(userId).setWithPriority({userName:userName, totalScore:totalScore}, totalScore)
Run Code Online (Sandbox Code Playgroud)
得分和总分数都是数字整数值.这就是我能想到的当前数据结构的所有细节.
Kat*_*ato 12
你的问题在技术上是如何使用安全规则来完成的,但由于这是一个XY问题,而且没有其他可能性被排除在外,我也会在这里解决其中的一些问题.
我将做出很多假设,因为回答这个问题实际上需要一套完整指定的规则需要遵循,而且实际上是一个实现整个应用程序的问题(增加分数是游戏逻辑规则的结果) ,不是一个简单的数学问题).
对这个难题最简单的答案可能就是没有总分.只需抓住玩家列表并手动累计.
如果这可能有用:
怎么做:
var ref = new Firebase(URL);
function getTotalScore(gameId, callback) {
ref.child('app/games/' + gameId + '/players').once('value', function(playerListSnap) {
var total = 0;
playerListSnap.forEach(function(playerSnap) {
var data = playerSnap.val();
total += data.totalScore || 0;
});
callback(gameId, total);
});
}
Run Code Online (Sandbox Code Playgroud)
一种非常复杂且简单的方法(因为它只需要将安全规则设置为类似的".write": "auth.uid === 'SERVER_PROCESS'")将使用仅监视游戏并累积总数的服务器进程.这可能是获得正确和最容易维护的最简单的解决方案,但却有需要另一个工作部分的缺点.
如果这可能有用:
怎么做:
显然,这涉及大量的应用程序设计,并且必须实现各种级别.让我们只关注结束游戏并统计排行榜,因为这是一个很好的例子.
首先将评分代码拆分为自己的路径,例如
/scores_entries/$gameid/$scoreid = < player: ..., score: ... >
/game_scores/$gameid/$playerid = <integer>
Run Code Online (Sandbox Code Playgroud)
现在监控游戏以查看它们何时关闭:
var rootRef = new Firebase(URL);
var gamesRef = rootRef.child('app/games');
var lbRef = rootRef.child('leaderboards');
gamesRef.on('child_added', watchGame);
gamesRef.child('app/games').on('child_remove', unwatchGame);
function watchGame(snap) {
snap.ref().child('status').on('value', gameStatusChanged);
}
function unwatchGame(snap) {
snap.ref().child('status').off('value', gameStatusChanged);
}
function gameStatusChanged(snap) {
if( snap.val() === 'CLOSED' ) {
unwatchGame(snap);
calculateScores(snap.name());
}
}
function calculateScores(gameId) {
gamesRef.child(gameId).child('users').once('value', function(snap) {
var userScores = {};
snap.forEach(function(ss) {
var score = ss.val() || 0;
userScores[ss.name()] = score;
});
updateLeaderboards(userScores);
});
}
function updateLeaderboards(userScores) {
for(var userId in userScores) {
var score = userScores[userId];
lbRef.child(userId).transaction(function(currentValue) {
return (currentValue||0) + score;
});
}
}
Run Code Online (Sandbox Code Playgroud)
当然,这将是可用选择中最复杂和最困难的.
如果这可能有用:
显然,我对这种方法有偏见.主要是因为很难做到正确,需要大量的能源,可以用小额的货币投资取而代之.
要做到这一点,需要仔细检查每个单独的写入请求.有几个明显的要点可以保证(可能更多):
以下是确保这些要点的一些基本原则:
举个例子,我们可以安全地更新排行榜.我们假设以下内容:
所以这是我们假设的数据结构:
/games/$gameid/users/$userid/score
/leaderboard_audit/$userid/$gameid/score
/leaderboard/$userid = { last_game: $gameid, score: <int> }
Run Code Online (Sandbox Code Playgroud)
以下是我们逻辑的工作原理:
/games/$gameid/users/$userid/score/leaderboard_audit/$userid/games_played/$gameid/leaderboard_audit/$userid/last_game更新值以匹配$gameidlast_game审计记录这是实际的规则:
{
"rules": {
"leaderboard_audit": {
"$userid": {
"$gameid": {
// newData.exists() ensures records cannot be deleted
".write": "auth.uid === $userid && newData.exists()",
".validate": "
// can only create new records
!data.exists()
// references a valid game
&& root.child('games/' + $gameid).exists()
// has the correct score as the value
&& newData.val() === root.child('games/' + $gameid + '/users/' + auth.uid + '/score').val()
// has a priority equal to the current timestamp
&& newData.getPriority() === now
// is created after the previous last_game or there isn't a last_game
(
!root.child('leaderboard/' + auth.uid + '/last_game').exists() ||
newData.getPriority() > data.parent().child(root.child('leaderboard/' + auth.uid + '/last_game').val()).getPriority()
)
"
}
}
},
"leaderboard": {
"$userid": {
".write": "auth.uid === $userid && newData.exists()",
".validate": "newData.hasChildren(['last_game', 'score'])",
"last_game": {
".validate": "
// must match the last_game entry
newData.val() === root.child('leaderboard_audit/' + auth.uid + '/last_game').val()
// must not be a duplicate
newData.val() !== data.val()
// must be a game created after the current last_game timestamp
(
!data.exists() ||
root.child('leaderboard_audit/' + auth.uid + '/' + data.val()).getPriority()
< root.child('leaderboard_audit/' + auth.uid + '/' + newData.val()).getPriority()
)
"
},
"score": {
".validate": "
// new score is equal to the old score plus the last_game's score
newData.val() === data.val() +
root.child('games/' + newData.parent().child('last_game').val() + '/users/' + auth.uid + '/score').val()
"
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2835 次 |
| 最近记录: |