2015-04-30 33 views
8

我最近在Thinkster上使用了关于使用Angular和Firebase创建Web应用程序的教程。使用Firebase实施唯一的用户名simplelogin

本教程使用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(); 
    }); 
}; 

在教程的结尾处,有一个 '下一个步骤' 部分,其包括:

强化用户名的唯一性 - 这是一个窍门Y,检查出火力地堡优先事项,并看看是否可以使用它们的用户名

我已经搜查,搜查查询用户配置文件,但无法找到如何做到这一点明确的解释,特别是在setPriority()方面Firebase的功能

我是Firebase的新手,所以这里的任何帮助都会很感激地收到。

有几个similar questions,但我似乎无法弄清楚如何解决这个问题。

提前致以巨大的感谢。


编辑

从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 
     } 
    }); 
}; 

但它抛出一个“undefined是不是一个函数”错误行var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username);。我已经将代码注释掉了,然后尝试了console.log(q),但仍然没有任何喜悦。

EDIT 2

与上面的问题是,Thinkster教程使用火力地堡0.8和orderByChild只有在以后的版本可用。更新和马林的答案是完美的。

+1

我不明白教程装置如何使用的优先级这一点,也许他们的意思[查询](https://www.firebase.com/docs/web/api/query /)。这些可用于检查客户端是否存在具有某个用户名的用户。此外,您可能希望查看服务器端安全规则,以防止编写已存在的用户名。 – Marein

+1

好点Marein,我认为thinkster教程的一部分可能是Firebase支持'orderByChild'之前的一个剩余部分。那时候你只有'setPriority',所以这将是唯一的查询机制。现在,我确实只需将用户'email'存储在他们的'/ profile/'节点,然后'ref.child('profile')。orderByChild('email')。equalTo(emailThatIsTryingToRegister).once('value')。 ..' –

+0

谢谢你们两个 @FrankvanPuffelen - 我想我理解这个概念,但你介意给我一些指导,以了解在哪里以及如何适应我现有的代码?另外,我认为在我的特定用途case我会使用'orderByChild('username')'? –

回答

16

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

在客户端,您需要检查用户名是否已经存在,以便在发送给服务器之前告诉用户他们的输入无效。确切位置在哪里你实现这个给你,但代码会是这个样子:

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" : { 
     ".validata" : "newData.isString()" 
     } 
    } 
    } 
} 

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

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

但是,看起来我们已经走了很远的一个洞来回答这个问题。也许我忽略了一些事情,事情比看起来简单。任何想法都表示赞赏。

此外,如果这个答案更像是一个问题而不是答案,我是新来的,不确定什么是合适的答案。

[1]虽然也许你可能会认为这不需要保证,因为恶意用户只会因为不声称自己的独特身份而伤害自己?

+0

真棒回答Marein! –

+1

谢谢Frank!虽然我觉得它有点开放式,你可能有什么补充吗? – Marein

+0

谢谢@Marein!初始解决方案将起作用,因为我的Firebase规则只允许在用户被授权时写入“个人档案”数据 - Firebase simplelogin单独处理电子邮件唯一性fr om我的数据库。就目前来看,一旦用户创建完成,配置文件就会被写入。当我到达一台计算机并报告时,我将在今晚测试代码!非常感谢这么彻底的回复 –

-1

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

Query for username unique in 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); 
      } 
     }); 
    };