【问题标题】:Firebase iOS Swift: Prevent Duplicate UsernamesFirebase iOS Swift:防止重复用户名
【发布时间】:2016-05-10 00:14:24
【问题描述】:

我正在构建一个使用 Firebase 的电子邮件和密码登录功能的应用。我让用户使用用户名、电子邮件和密码进行注册。如果用户名不是唯一的,我正在努力如何阻止创建用户。我一直在阅读其他问题(特别是 Firebase-android-make-username-uniquehow-prevent-username-from-duplicate-signup-infirebase),但我仍然没有让它完全发挥作用。

我按照上面第一个链接中的说明将我的数据结构设置为:

app : {
    users: {
       "some-user-uid": {
            email: "test@test.com"
            username: "myname"
       }
    },
    usernames: {
        "myname": "some-user-uid"
    }
}

我的安全规则如下:

"users": {
  "$uid": {
    ".write": "auth !== null && auth.uid === $uid",
    ".read": "auth !== null && auth.provider === 'password'",
    "username": {
      ".validate": "
        !root.child('usernames').child(newData.val()).exists() ||
        root.child('usernames').child(newData.val()).val() == $uid"
    }
  }
}

使用此设置,如果我尝试使用已存在的用户名创建新用户,则会阻止该用户被添加到我的数据结构中。调用以下代码时,如果用户名重复,则会打印“无法保存用户数据”。

  func createNewAccount(uid: String, user: Dictionary<String, String>) {

    USER_REF.childByAppendingPath(uid).setValue(user, withCompletionBlock: {
      (error:NSError?, ref:Firebase!) in
      if (error != nil) {
        print("User Data could not be saved.")
      } else {
        print("User Data saved successfully!")
      }
    })
  }

  func addUsernameToUsernamePath (userData: Dictionary<String, String>) {

    USERNAME_REF.updateChildValues(userData)
  }

这就是我卡住的地方。我下面的创建帐户方法在调用 createUser 和 authUser 之前不会调用上述两种方法(我需要获取 uid)。我的问题是用户仍然被创建为注册用户,而我的安全规则只是阻止用户信息被添加到我的数据结构中。如果用户名重复,我需要弄清楚如何阻止创建用户。

@IBAction func createAccount() {
    let username = usernameField.text
    let email = emailField.text
    let password = passwordField.text

    if username != "" && email != "" && password != "" {

      // Set Email and Password for the New User.

      DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in

        if error != nil {
          print("Error: \(error)")
          if let errorCode = FAuthenticationError(rawValue: error.code) {
            switch (errorCode) {
            case .EmailTaken:
              self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.")
            default:
              self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.")
            }
          }

        } else {

          DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: {
            err, authData in


            let user = ["provider": authData.provider!, "email": email!, "username": username!]
            let userData = [username!: authData.uid!]

            DataService.dataService.createNewAccount(authData.uid, user: user)
            DataService.dataService.addUsernameToUsernamePath(userData)          

          })

编辑

这是我更新后的 createAccount 方法,它解决了我的问题。

  @IBAction func createAccount() {
    let username = usernameField.text
    let email = emailField.text
    let password = passwordField.text


if username != "" && email != "" && password != "" {

  DataService.dataService.USERNAME_REF.observeEventType(.Value, withBlock: { snapshot in
    var usernamesMatched = false
    if snapshot.value is NSNull {
      usernamesMatched = false
    } else {
      let usernameDictionary = snapshot.value
      let usernameArray = Array(usernameDictionary.allKeys as! [String])
      for storedUserName in usernameArray {
        if storedUserName == self.usernameField.text! {
          usernamesMatched = true
          self.signupErrorAlert("Username Already Taken", message: "Please try a different username")
        }
      }
    }

    if !usernamesMatched {
      // Set Email and Password for the New User.

      DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in

        if error != nil {
          print("Error: \(error)")
          if let errorCode = FAuthenticationError(rawValue: error.code) {
            switch (errorCode) {
            case .EmailTaken:
              self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.")
            default:
              self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.")
            }
          }

        } else {

          // Create and Login the New User with authUser
          DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: {
            err, authData in


            let user = ["provider": authData.provider!, "email": email!, "username": username!]
            let userData = [username!: authData.uid!]

            // Seal the deal in DataService.swift.
            DataService.dataService.createNewAccount(authData.uid, user: user)
            DataService.dataService.addUsernameToUsernamePath(userData)


          })

【问题讨论】:

  • 在调用 createUser() 之前验证用户名不存在?
  • 感谢您的建议。我刚刚更新了我的代码,它似乎已经解决了这个问题。
  • 为什么不先通过createUser->FAuthenticationErrorEmailTaken 检查重复的用户用户名,如果可用,然后在/users 节点中创建用户?如果您这样做,您可以将该代码减少到几行,因为该用户名保证不存在于您的 /users 节点中。
  • 检查重复的电子邮件地址。 createUser 只有电子邮件和密码的参数。我还在为每个用户创建一个用户名。在我的应用程序中,我让用户输入用户名、电子邮件和密码来创建帐户。我不相信 createUser 有办法检查重复的用户名。我确实设置了 FAuthenticationErrorEmailTaken 来检查重复的电子邮件地址。

标签: ios swift firebase


【解决方案1】:

您可以允许在没有有效用户名的情况下注册,并在部分注册的情况下显示一个单独的“设置用户名”屏幕。

定义您的安全规则以在允许写入数据库的其他部分之前检查非空用户名。

【讨论】:

  • 感谢您的建议。我刚刚找到了一种解决方法,方法是在调用 createUser() 之前检查用户名是否不存在。不过,我很欣赏这个建议,如果我对我的解决方案不满意,我可能会实施它。
  • 你可能想要同时做这两个,否则如果两个人同时注册同一个用户名,你就会有竞争条件。
  • 好点。我刚刚尝试在两部 iPhone 上创建两个具有相同用户名的帐户。当我在同一时间触摸创建帐户时,我遇到了问题。
【解决方案2】:

我可以通过将 createAccount() 更新为下面的代码来使其正常工作。

  @IBAction func createAccount() {
    let username = usernameField.text
    let email = emailField.text
    let password = passwordField.text


    if username != "" && email != "" && password != "" {

      // Checks for internet connection before saving the meetup. Returns if there is no internet connection.
      let reachability = try! Reachability.reachabilityForInternetConnection()

      if reachability.currentReachabilityStatus == .NotReachable {
        let internetAlert = UIAlertController(title: "No Internet Connection", message: "Please make sure your device is connected to the internet.", preferredStyle: .Alert)
        let internetAlertAction = UIAlertAction(title: "OK", style: .Default, handler: nil)
        internetAlert.addAction(internetAlertAction)
        presentViewController(internetAlert, animated: true, completion: nil)
        return
      }
      DataService.dataService.USERNAME_REF.observeEventType(.Value, withBlock: { snapshot in
        var usernamesMatched = false
        if snapshot.value is NSNull {
          usernamesMatched = false
        } else {
          let usernameDictionary = snapshot.value
          let usernameArray = Array(usernameDictionary.allKeys as! [String])
          for storedUserName in usernameArray {
            if storedUserName == self.usernameField.text! {
              usernamesMatched = true
              self.signupErrorAlert("Username Already Taken", message: "Please try a different username")
            }
          }
        }

        if !usernamesMatched {
          // Set Email and Password for the New User.

          DataService.dataService.BASE_REF.createUser(email, password: password, withValueCompletionBlock: { error, result in

            if error != nil {
              print("Error: \(error)")
              if let errorCode = FAuthenticationError(rawValue: error.code) {
                switch (errorCode) {
                case .EmailTaken:
                  self.signupErrorAlert("Email In Use", message: "An account has already been created for this email address.")
                default:
                  self.signupErrorAlert("Oops!", message: "Having some trouble creating your account. Please try again or check your internet connection.")
                }
              }

            } else {

              // Create and Login the New User with authUser
              DataService.dataService.BASE_REF.authUser(email, password: password, withCompletionBlock: {
                err, authData in


                let user = ["provider": authData.provider!, "email": email!, "username": username!]
                let userData = [username!: authData.uid!]

                // Seal the deal in DataService.swift.
                DataService.dataService.createNewAccount(authData.uid, user: user)
                DataService.dataService.addUsernameToUsernamePath(userData)


              })

【讨论】:

    猜你喜欢
    • 2017-06-08
    • 1970-01-01
    • 1970-01-01
    • 2016-08-23
    • 2021-06-27
    • 2016-09-28
    • 2017-02-27
    • 2017-08-19
    • 2012-03-24
    相关资源
    最近更新 更多