如何防止 Firebase 中出现重复的用户属性?

Posted

技术标签:

【中文标题】如何防止 Firebase 中出现重复的用户属性?【英文标题】:How do you prevent duplicate user properties in Firebase? 【发布时间】:2014-10-07 07:17:26 【问题描述】:

我正在使用FirebaseSimpleLogin 创建用户并处理身份验证。

当我尝试通过 $createUser() 方法通过简单登录创建新用户时,如果电子邮件地址已被使用,则 firebase 不会创建用户。但是,我还使用$set() 在创建用户后将其保存到我的firebase,并且我使用user.uid 作为密钥。当尝试写入数据库时​​,即使用户名不是唯一的,firebase 也会保存记录,因为简单登录只需要电子邮件和密码。那么,当用户名不被用作用户对象的键时,如何验证用户名是唯一的呢?

我正在创建这样的新用户:

$scope.createUser = function() 
  $scope.auth.$createUser('trinker@gmail.com', 'password').then(function(user, err) 
    if (!err) 
      ref.child('users/' + user.uid).set(
        email: user.email,
        username: user.username
      );
      console.log("success!");
    else
      console.log(err.message);
    
  );

我的用户对象如下所示:


  "users" : 
    "simplelogin:28" : 
      "email" : "trinker@gmail.com",
      "username" : "jtrinker"
    ,
    "simplelogin:30" : 
      "email" : "test@gmail.com",
      "username" : "jtrinker"
    
  



我需要使用 uid 作为每个用户的键,但我仍然需要用户名是唯一的。

如果一个对象中的属性不是另一个对象中的属性所独有的,我该如何防止 firebase 保存记录?

【问题讨论】:

如果他们没有成功创建帐户,为什么要写用户记录?为什么未经身份验证的用户会写入记录?您能否详细说明为什么这是一个好主意? 我想我不清楚。正如您明确暗示的那样,显然未经身份验证的用户写入记录是一个非常糟糕的主意。然而,这不是重点。关键是,由于创建用户帐户不需要用户名,我如何确保我的 firebase 中没有重复的用户名? 您的问题询问如何使电子邮件地址独一无二。如果电子邮件地址已被占用,他们将无法创建帐户,因此无法进行身份验证,这将阻止为重复的电子邮件地址创建用户帐户。所以我仍然没有完全掌握我们试图解决的更大的用例。 我很抱歉造成混乱。在我的问题结束时,我提到我还需要用户拥有一个用户名,该用户名也需要是唯一的。是的,所有电子邮件地址都是唯一的,因为如果电子邮件已经被接收,他们将无法创建帐户,但是,firebase 简单登录不需要用户名,因此有人可以使用唯一的电子邮件创建新用户地址但输入重复的用户名。我想在上面的用户对象中添加一个用户名键,但我需要确保你不能写给用户,除非用户名在我的 firebase 的所有用户中也是唯一的。 我编辑了我的问题希望更清楚。 【参考方案1】:

首先,如果用户已经拥有username,它是独一无二的,并且不会消失,我建议您放弃使用简单登录uids。正如您在这里已经发现的那样,这只会产生试图在两者之间来回切换的问题。使用firebase-passport-login 之类的工具调查creating your own tokens,然后通过username 存储记录。

但既然这不是你的问题,让我们在我们在这里的时候解决这个问题,因为你可能想继续前进,进入我多次经过的双重身份的荆棘荆棘。

要使用户名唯一,请存储用户名索引。

/users/$userid/username/$username
/usernames/$username/$userid

为确保它们是唯一的,请在 usernames/ 路径中的用户 id 上添加如下安全规则,以确保每个用户名只能分配一个用户,并且值是用户的 id:

".write": "newData.val() === auth.uid && !data.exists()"

现在通过将以下内容添加到 users/ 记录中的用户名来强制它们匹配:

"users": 
   "$userid": 
      "username": 
         ".validate": "root.child('usernames/'+newData.val()).val() === $userid"
      
   

这将确保 ID 是唯一的。小心读取权限。您可能希望完全避免这些,因为您不希望任何人查找私人电子邮件或用户名。 Something like I demonstrated in support 保存这些将是理想的。

这里的想法是您尝试分配用户名和电子邮件,如果它们失败,那么它们已经存在并且属于另一个用户。否则,您将它们插入到用户记录中,现在让用户按 uid 和电子邮件进行索引。

为了符合 SO 协议,以下是该要点的代码,最好通过链接阅读:

var fb = new Firebase(URL);

function escapeEmail(email) 
   return email.replace('.', ',');


function claimEmail(userId, email, next) 
   fb.child('email_lookup').child(escapeEmail(email)).set(userId, function(err) 
      if( err )  throw new Error('email already taken'); 
      next();
   );


function claimUsername(userId, username, next) 
   fb.child('username_lookup').child(username).set(userId, function(err) 
      if( err )  throw new Error('username already taken'); 
      next();
   );   


function createUser(userId, data) 
   claimEmail(userId, data.email, claimUsername.bind(null, userId, data.username, function() 
      fb.child('users').child(userId).set(data);
   );   

还有规则:


  "rules": 
     "users": 
        "$user": 
           "username": 
               ".validate": "root.child('username_lookup/'+newData.val()).val() === auth.uid"
           ,
           "email": 
               ".validate": "root.child('email_lookup').child(newData.val().replace('.', ',')).val() === auth.uid"
           
        
     ,

     "email_lookup": 
        "$email": 
           // not readable, cannot get a list of emails!
           // can only write if this email is not already in the db
           ".write": "!data.exists()",
           // can only write my own uid into this index
           ".validate": "newData.val() === auth.uid"
        
     ,
     "username_lookup": 
        "$username": 
           // not readable, cannot get a list of usernames!
           // can only write if this username is not already in the db
           ".write": "!data.exists()",

           // can only write my own uid into this index
           ".validate": "newData.val() === auth.uid"
        
     ,
  

【讨论】:

非常感谢您的回答。两个问题:1)email_lookupusername_lookup 对象指的是什么?我以前没见过。它在任何文档中吗?并且 2) 如果您使用 firebase-passport 进行自定义登录,您会使用 username 作为密钥,而不是 id?谢谢! email_lookup 和 username_lookup 只是一个包含 username -> userid 和 email -> userid 的哈希。 我想更具体地说我的问题是email_lookupusername_lookup 是什么?它们不像"rules:" 下的其他项目那样是数据。它们充当函数吗? 在我给出的示例中,它们是数据(我称它们为索引)。它们由电子邮件/用户名键入,并具有简单登录 uid 的值。它们由 claimEmail() 和 claimUsername() 方法编写。 我在这里看到的唯一问题是它允许用户声明多个用户名/电子邮件 - 他们不能一次使用多个,但会阻止其他用户使用它们。但是,如果不使用自定义令牌,似乎真的没有更好的解决方案。【参考方案2】:

使用安全规则检查它的存在不是更容易吗?我的设置如下:

"usernames": 
  "$usernameid": 
    ".read": "auth != null",
        ".write": "auth != null  && (!data.exists() || !newData.exists())"
    
    

如果用户名不存在,则允许写入。我相信我是直接从 Firebase 文档中得到的。

【讨论】:

你能告诉我$usernameid是什么吗?因为我刚开始使用 firebase 。我也从那里不明白。

以上是关于如何防止 Firebase 中出现重复的用户属性?的主要内容,如果未能解决你的问题,请参考以下文章

如何防止用户重复提交数据

如何检测用户是不是第一次在 Firebase [重复]

如何防止同一用户同时登录 Firebase?

如何防止 Firebase 一遍又一遍地创建同一个用户?

检查firebase子项是不是具有特定值[重复]

php防止用户重复提交表单