使用Firebase simplelogin 强制唯一的用户名
使用Firebase simplelogin 强制唯一的用户名
最近我在Thinkster上参考了一个教程,学习使用Angular和Firebase来创建一个Web应用程序。
教程使用了Firebase的simpleLogin方法,允许创建一个包含用户名的'profile'。
工厂:
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的答案,我已经更新了控制器中的register函数为:
$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 { // 用户名已存在,请用户输入一个不同的名称 } }); };
但是,在var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username);
这一行出现了'undefined is not a function'的错误。我已经将后面的代码注释掉并尝试了console.log(q),但仍然没有成功。
编辑 2
以上问题的原因是Thinkster教程使用的是Firebase 0.8版本,而orderByChild只在较新的版本中可用。更新了版本后,Marein的答案是完美的。
在使用Firebase simplelogin进行用户注册后,我遇到了一个类似的问题。在用户配置文件中,我需要保存一个唯一的用户名,但是我找到了一个解决方法,也许这对你有用。
问题出现的原因是,我需要确保用户输入的用户名在数据库中是唯一的。为了解决这个问题,我使用了Firebase的orderByChild和equalTo方法来查询数据库中是否已存在相同的用户名。如果存在相同的用户名,我会将一个布尔值变量used设置为true,表示用户名已被使用。然后,我使用once方法来获取数据库中的数据,并根据used变量的值来判断是否需要更新用户名。
下面是代码示例:
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) { 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); } }); };
通过以上代码,我成功解决了在Firebase simplelogin中强制实现唯一用户名的问题。希望这个解决方法对你也有帮助。
Firebase simplelogin是一个用于管理用户认证和授权的Firebase插件。在使用Firebase simplelogin时,你可能会遇到一个问题,即如何强制用户名的唯一性。问题的原因以及解决方法。
问题的原因是,当用户在客户端输入用户名时,需要在发送到服务器之前检查该用户名是否已存在。在客户端,可以使用以下代码来实现这个检查:
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) { // 用户名不存在,可以添加新用户 } else { // 用户名已存在,请用户选择不同的名称 } });
然而,如果用户恶意使用JS控制台来写入服务器,上述的客户端检查就无效了。为了防止这种情况发生,需要在服务器端实施安全措施。
然而,当尝试提供一个解决方案时,遇到了一个问题。假设数据库的结构如下:
{ "profiles" : { "profile1" : { "username" : "Nick", "md5_hash" : "..." }, "profile2" : { "username" : "Marein", "md5_hash" : "..." } } }
希望在添加新的profile时,能够确保没有具有相同username属性的profile对象存在。然而,据所知,Firebase安全语言不支持这种数据结构的规则。
解决方法是改变数据结构,使用username作为每个profile的键(而不是profile1、profile2等)。这样,就只能有一个具有该用户名的对象存在。数据库结构将变为:
{ "profiles" : { "Nick" : { "md5_hash" : "..." }, "Marein" : { "md5_hash" : "..." } } }
这在这种情况下可能是一个可行的解决方案。然而,如果不仅username,还有email也需要唯一,该怎么办呢?除非我们使用字符串拼接,否则它们不能都成为对象的键。
另一个想法是,除了profiles列表外,还保持一个单独的usernames列表和一个单独的emails列表。然后可以在安全规则中轻松地使用它们来检查给定的username和email是否已存在。安全规则将如下所示:
{ "rules" : { ".write" : true, ".read" : true, "profiles" : { "$profile" : { "username" : { ".validate" : "!root.child('usernames').child(newData.val()).exists()" } } }, "usernames" : { "$username" : { ".validate" : "newData.isString()" } } } }
然而,现在我们又遇到了另一个问题:如何确保在创建新的profile时,用户名(和email)也被放入这些新的列表中?
这个问题可以通过将profile创建的代码从客户端移到服务器上来解决。客户端将需要向服务器请求创建新的profile,而服务器将确保执行所有必要的任务。
然而,我们似乎走得太远来回答这个问题了。也许我忽视了一些简单的解决方法。欢迎任何想法。
总结起来,解决强制用户名唯一性的问题,可以在客户端进行检查并在服务器端实施安全规则。改变数据结构或者使用单独的列表来记录用户名和email的唯一性也是解决方案之一。将profile创建的代码放在服务器上也可以解决添加新的用户名和email到列表的问题。