使用Firebase simplelogin强制执行唯一用户名



我最近在Thinkster上学习了一个使用Angular和Firebase创建web应用程序的教程。

本教程使用Firebase simpleLogin方法,允许创建包含用户名的"配置文件"。

工厂:

app.factory('Auth', function($firebaseSimpleLogin, $firebase, FIREBASE_URL, $rootScope) {
var ref = new Firebase(FIREBASE_URL);

var auth = $firebaseSimpleLogin(ref);
var Auth = {
register: function(user) {
return auth.$createUser(user.email, user.password);
},
createProfile: function(user) {
var profile = {
username: user.username,
md5_hash: user.md5_hash
};
var profileRef = $firebase(ref.child('profile'));
return profileRef.$set(user.uid, profile);
},
login: function(user) {
return auth.$login('password', user);
},
logout: function() {
auth.$logout();
},
resolveUser: function() {
return auth.$getCurrentUser();
},
signedIn: function() {
return !!Auth.user.provider;
},
user: {}
};
$rootScope.$on('$firebaseSimpleLogin:login', function(e, user) {
angular.copy(user, Auth.user);
Auth.user.profile = $firebase(ref.child('profile').child(Auth.user.uid)).$asObject();
console.log(Auth.user);
});
$rootScope.$on('$firebaseSimpleLogin:logout', function() {
console.log('logged out');
if (Auth.user && Auth.user.profile) {
Auth.user.profile.$destroy();
}
angular.copy({}, Auth.user);
});
return Auth;
});

控制器:

$scope.register = function() {
Auth.register($scope.user).then(function(user) {
return Auth.login($scope.user).then(function() {
user.username = $scope.user.username;
return Auth.createProfile(user);
}).then(function() {
$location.path('/');
});
}, function(error) {
$scope.error = error.toString();
});
};

在教程的最后有一个"下一步"部分,其中包括:

强制用户名唯一性——这一点很棘手,请查看Firebase优先级,看看是否可以使用它们按用户名查询用户配置文件

我搜索了又搜索,但找不到如何做到这一点的明确解释,特别是在Firebase 的setPriority()函数方面

我是Firebase的新手,所以这里的任何帮助都将不胜感激。

还有一些类似的问题,但我似乎不知道如何解决。

非常感谢。


编辑

根据Marein的回答,我已将控制器中的寄存器功能更新为:

$scope.register = function() {
var ref = new Firebase(FIREBASE_URL);
var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username);
q.once('value', function(snapshot) {
if (snapshot.val() === null) {
Auth.register($scope.user).then(function(user) {
return Auth.login($scope.user).then(function() {
user.username = $scope.user.username;
return Auth.createProfile(user);
}).then(function() {
$location.path('/');
});
}, function(error) {
$scope.error = error.toString();
});
} else {
// username already exists, ask user for a different name
}
});
};

但它在var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username);行中抛出了一个"未定义不是函数"错误。我在之后评论了代码,并尝试了console.log(q),但仍然没有乐趣。

编辑2

上面的问题是Thinkster教程使用Firebase 0.8,orderByChild仅在更高版本中可用。更新和Marein的答案是完美的。

这里有两件事要做,客户端检查和服务器端规则。

在客户端,您需要检查用户名是否已经存在,以便在将其发送到服务器之前告诉用户他们的输入无效。具体在哪里实现取决于您,但代码看起来像这样:

var ref = new Firebase('https://YourFirebase.firebaseio.com');
var q = ref.child('profiles').orderByChild('username').equalTo(newUsername);
q.once('value', function(snapshot) {
if (snapshot.val() === null) {
// username does not yet exist, go ahead and add new user
} else {
// username already exists, ask user for a different name
}
});

在写入服务器之前,您可以使用它进行检查。然而,如果用户是恶意的,并且决定使用JS控制台向服务器写入,该怎么办?为了防止这种情况,您需要服务器端安全性。

我试图想出一个解决方案的例子,但遇到了一个问题。希望有一个更有见识的人会出现。我的问题如下。假设您的数据库结构如下:

{
"profiles" : {
"profile1" : {
"username" : "Nick",
"md5_hash" : "..."
},
"profile2" : {
"username" : "Marein",
"md5_hash" : "..."
}
}
}

添加新配置文件时,您需要有一个规则来确保不存在具有相同username属性的配置文件对象。然而,据我所知,Firebase安全语言不支持这种数据结构。

一个解决方案是更改数据结构,使用username作为每个配置文件的密钥(而不是profile1profile2…)。这样,就只能自动有一个对象使用该用户名。数据库结构为:

{
"profiles" : {
"Nick" : {
"md5_hash" : "..."
},
"Marein" : {
"md5_hash" : "..."
}
}
}

在这种情况下,这可能是一个可行的解决方案。然而,如果不仅用户名,例如电子邮件也必须是唯一的,该怎么办?它们不能都是对象键(除非我们使用字符串串联…)

我想到的另一件事是,除了个人资料列表之外,还要保留一个单独的用户名列表和一个单独电子邮件列表。然后,这些可以很容易地在安全规则中使用,以检查给定的用户名和电子邮件是否已经存在。规则看起来像这样:

{
"rules" : {
".write" : true,
".read" : true,
"profiles" : {
"$profile" : {
"username" : {
".validate" : "!root.child('usernames').child(newData.val()).exists()"
}
}
},
"usernames" : {
"$username" : {
".validate" : "newData.isString()"
}
}
}
}

然而,现在我们遇到了另一个问题;如何确保在创建新的配置文件时,用户名(和电子邮件)也被放入这些新列表中[1]

这反过来可以通过将配置文件创建代码从客户端中取出并放在服务器上来解决。然后,客户端需要要求服务器创建一个新的配置文件,服务器将确保执行所有必要的任务。

然而,在回答这个问题上,我们似乎已经陷入了困境。也许我忽略了一些事情,事情比看起来更简单。任何想法都值得赞赏。

此外,很抱歉,如果这个答案更像是一个问题而不是一个答案,我是SO的新手,还不确定什么是合适的答案。

[1]尽管你可能会争辩说这不需要确保,因为恶意用户不声称自己的唯一身份只会伤害自己

我也遇到了类似的问题。但这是在用密码和电子邮件注册用户之后。在用户配置文件中可以保存一个必须唯一的用户名,我已经找到了一个解决方案,也许这可以为您服务。

查询Firebase 中唯一的用户名

var ref = new Firebase(FIREBASE_URL + '/users');
ref.orderByChild("username").equalTo(profile.username).on("child_added", function(snapshot) {
if (currentUser != snapshot.key()) {
scope.used = true;
}   
}); 
ref.orderByChild("username").equalTo(profile.username).once("value", function(snap) {
//console.log("initial data loaded!", Object.keys(snap.val()).length === count);
if (scope.used) {
console.log('username already exists');
scope.used = false;
}else{
console.log('username doesnt exists, update it');
userRef.child('username').set(profile.username);
}   
}); 
};  

最新更新