使用Firebase simplelogin实施唯一的用户名

Nic*_*ton 9 angularjs firebase angularfire

我最近在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;
});
Run Code Online (Sandbox Code Playgroud)

控制器:

$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();
    });
};
Run Code Online (Sandbox Code Playgroud)

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

实施用户名唯一性 - 这个很棘手,请查看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
        }
    });
};
Run Code Online (Sandbox Code Playgroud)

但它在行中抛出了"未定义的不是函数"错误var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username);.我已经注释掉了代码并尝试了console.log(q),但仍然没有乐趣.

编辑2

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

Mar*_*ein 17

这里有两件事要做,一个是客户端检查,另一个是服务器端规则.

在客户端,您要检查用户名是否已存在,以便在将用户的输入发送到服务器之前告诉用户他们的输入无效.你在哪里准确地实现了这一点,但代码看起来像这样:

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
  }
});
Run Code Online (Sandbox Code Playgroud)

您可以在写入服务器之前使用它来检查.但是,如果用户是恶意的并决定使用JS控制台写入服务器呢?为防止这种情况,您需要服务器端安全性.

我试图提出一个示例解决方案,但我遇到了一个问题.希望有更多知识渊博的人会来.我的问题如下.假设您的数据库结构如下所示:

{
  "profiles" : {
    "profile1" : {
      "username" : "Nick",
      "md5_hash" : "..."
    },
    "profile2" : {
      "username" : "Marein",
      "md5_hash" : "..."
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

添加新配置文件时,您需要一个规则,确保不username存在具有相同属性的配置文件对象.但是,据我所知,Firebase安全语言不支持此功能,具有此数据结构.

一个解决办法是改变数据结构使用username为重点,每个配置文件(而不是profile1,profile2......).这样,自动只能有一个具有该用户名的对象.数据库结构将是:

{
  "profiles" : {
    "Nick" : {
      "md5_hash" : "..."
    },
    "Marein" : {
      "md5_hash" : "..."
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,这可能是可行的解决方案.但是,如果不仅是用户名,而且例如电子邮件也必须是唯一的?它们不能都是对象键(除非我们使用字符串连接...).

我想到的另一件事是,除了配置文件列表之外,还要保留一个单独的用户名列表和一个单独的电子邮件列表.然后可以在安全规则中轻松使用这些内容来检查给定的用户名和电子邮件是否已存在.规则看起来像这样:

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

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

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

然而,似乎我们已经走得很远,无法回答这个问题.也许我忽略了一些东西,事情比他们看起来更简单.任何想法都表示赞赏.

另外,如果这个答案更像是一个问题而不是一个答案,那么道歉,我是SO的新手并不确定什么是合适的答案.

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