我可以让 Firebase 使用用户名登录过程吗?

Posted

技术标签:

【中文标题】我可以让 Firebase 使用用户名登录过程吗?【英文标题】:Can I make Firebase use a username login process? 【发布时间】:2016-09-29 08:35:55 【问题描述】:

我想制作一个允许用户名登录的系统。此过程需要以下内容:

    用户必须使用电子邮件/密码注册 用户可以设置唯一的用户名 用户可以使用电子邮件或用户名登录 用户可以通过邮箱或用户名找回密码 该功能必须在启用持久性的数据库上运行

这个问题之前已经回答过了,但是它禁用了用户使用密码恢复的功能。他们也没有解决区分大小写的问题,一个人可以注册为“scooby”,另一个人可以注册为“Scooby”。

【问题讨论】:

【参考方案1】:

免责声明:此代码现已使用两年多。虽然这并不意味着它已被弃用,但我强烈建议在假设这是最好的方法之前研究替代方法。我个人不希望 Firebase 的标准登录过程由我最初解决问题的方法决定,而 Firebase 并没有像现在这样被广泛采用。



经过多次开发迭代,我提出了以下设计来解决这个问题。我将在 Swift 中发布我的代码 sn-ps,但它们通常可以轻松地直接翻译成 android


    为电子邮件/密码创建 Firebase 注册流程。

这是用户登录体验的支柱。这可以完全从提供的库存 Firebase API 文档中实现 here


    提示用户输入用户名

用户名输入应在注册时完成,我建议在注册流程中添加一个附加字段。我还建议在用户登录时检查用户是否有用户名。如果没有,则显示一个 SetUsername 界面,提示他们在进一步进入 UI 之前设置用户名。由于某些原因,用户可能没有用户名;它可能会因为粗鲁或保留而被撤销,或者他们可能在注册时需要用户名之前就注册了。

如果您使用的是启用持久性的 Firebase 构建,请确保使用 Firebase Transactions。事务是必要的,否则您的应用可以对用户名表中的数据进行假设,即使用户名可能仅在几秒钟前就已为用户设置。

我还建议强制用户名接近字母数字(我允许使用一些无害的标点符号)。在 Swift 中,我可以使用以下代码实现这一点:

static var invalidCharacters:NSCharacterSet 
    let chars = NSMutableCharacterSet.alphanumericCharacterSet()

    // I add _ - and . to the valid characters.
    chars.addCharactersInString("_-.")

    return chars.invertedSet


if username.rangeOfCharacterFromSet(invalidCharacters) != nil 
    // The username is valid


    保存用户数据

下一个重要步骤是了解如何以我们将来可以访问的方式保存用户的数据。以下是我存储用户数据的方式的屏幕截图: 需要注意的几点:

用户名存储两次,一次在usernames 中,另一次在details/[uid]/username 中。我建议这样做,因为它允许您对用户名区分大小写(请参阅下一点),并且它还允许您知道确切的数据库引用以检查用户名 (usernames/scooby),而不必查询或检查details 查找匹配的用户名(当您必须考虑区分大小写时,这只会变得更加复杂) usernames 引用以小写形式存储。当我检查此引用中的值或保存到此引用时,我确保仅以小写形式保存数据。这意味着如果有人想检查用户名“scoobY”是否存在,它将失败,因为在小写中它与现有用户“Scooby”的用户名相同。 details/[uid]/username 字段包含大写字母。这允许在用户偏好的情况下显示用户名,而不是强制使用小写或大写的单词,用户可以将其名称指定为“NASA Fan”而不转换为“Nasa Fan”,同时还防止注册用户名“NASA FAN”(或任何其他案例迭代)的任何其他人 电子邮件存储在用户详细信息中。这可能看起来很奇怪,因为您可以通过Firebase.auth().currentUser.email? 检索当前用户的电子邮件。这是必要的原因是因为我们需要在以用户身份登录之前引用电子邮件。
    使用电子邮件或用户名登录

为了无缝地工作,您需要在登录时加入一些检查。

由于我不允许在用户名中使用 @ 字符,我可以假设包含 @ 的登录请求是电子邮件请求。这些请求会使用 Firebase 的 FIRAuth.auth().signInWithEmail(email, password, completion) 方法正常处理。

对于所有其他请求,我们将假定它是用户名请求。 注意:转换为小写。

let ref = FIRDatabase.database().reference()
let usernameRef = ref.child("users/usernames/\(username.lowercaseString)")

当您执行此检索时,您应该考虑是否启用了持久性,以及是否有可能撤销用户名。如果可以撤销用户名并且您启用了持久性,您将需要确保在 Transaction 块中检索用户名值,以确保不会返回缓存值。

当此检索成功时,您从username[username] 中获取值,即用户的uid。使用此值,您现在可以对用户的电子邮件值执行检索:

let ref = FIRDatabase.database().reference()
let usernameRef = ref.child("users/details/[uid]/email")

一旦此请求成功,您就可以使用刚刚检索到的电子邮件字符串执行标准 Firebase 电子邮件登录。

完全相同的检索方法可用于从用户名中检索电子邮件以恢复密码。

高级功能需要注意的几点: - 如果您允许用户使用 FIRUserProfileChangeRequest 更新他们的电子邮件,请确保在 auth 和 details[uid]email 字段上都更新它,否则您将破坏用户名登录功能 - 通过使用成功和失败块,您可以显着减少处理检索方法中所有不同失败案例所需的代码。这是我的获取电子邮件方法的示例:

static func getEmail(username:String, success:(email:String) -> Void, failure:(error:String!) -> Void) 
    let usernameRef = FIRDatabase.database().reference().child("users/usernames/\(username.lowercaseString)")

    usernameRef.observeSingleEventOfType(.Value, withBlock:  (snapshot) in
        if let userId = snapshot.value as? String 
            let emailRef = FIRDatabase.database().reference().child("users/details/\(userId)/email")

            emailRef.observeSingleEventOfType(.Value, withBlock:  (snapshot) in
                if let email = snapshot.value as? String 
                    success(email: email)
                 else 
                    failure(error: "No email found for username '\(username)'.")
                
            )  (error) in
                failure(error: "Email could not be found.")
            
         else 
            failure(error: "No account found with username '\(username)'.")
        
    )  (error) in
        failure(error: "Username could not be found.")
    

这个成功/失败块实现让我在 ViewControllers 中调用的代码更加简洁。 Å login 调用如下方法:

if fieldText.containsString("@") 
    loginWithEmail(fieldText)
 else 
    // Attempt to get email for username.
    LoginHelper.getEmail(fieldText, success:  (email) in
        self.loginWithEmail(email)
    , failure:  error in
        HUD.flash(.Error, delay: 0.5)
    )

【讨论】:

干得好,先生!如果您发现这方面的问题,请及时通知我们,我也会这样做。 一直在尝试使用自定义身份验证来解决这个问题,但现在我不必使用您的天才解决方案!干得好! 我现在很困惑,因为我无法使用自定义登录来实现用户名。 在找到您的解决方案之前,我正在循环所有我知道会很昂贵的东西,但感谢您的天才想法。

以上是关于我可以让 Firebase 使用用户名登录过程吗?的主要内容,如果未能解决你的问题,请参考以下文章

如果我在 firebase 身份验证中有很多冗余匿名用户,可以吗? [关闭]

如果用户已经无密码登录,firebase 可以设置密码身份验证吗?

Firebase 使用 idToken 或 refreshToken 登录

使用 swift 将数据写入 Firebase

Firebase:相同的用户名

Firebase:如何让 Android 用户保持登录状态?