问题
我已经多次看到这个问题(也在 Firebase 实时数据库的上下文中),但我还没有看到令人信服的答案。问题陈述相当简单:
(经过身份验证的)用户如何选择尚未被占用的用户名?
首先,why:用户通过身份验证后,拥有唯一的用户ID。然而,许多网络应用程序让用户选择“显示名称”(用户希望在网站上显示的方式),以保护用户的个人数据(例如真实姓名)。
用户集合
给定如下的数据结构,可以为每个用户存储用户名以及其他数据:
/users (collection)
/{uid} (document)
- name: "<the username>"
- foo: "<other data>"
然而,没有什么可以阻止另一个用户(具有不同的{uid}
)存储相同的name
在他们的记录中。据我所知,没有任何“安全规则”可以让我们检查是否name
已被其他用户使用。
注意:客户端检查是可能的,但不安全,因为恶意客户端可能会忽略检查。
反向映射
流行的解决方案是创建具有反向映射的集合:
/usernames (collection)
/{name} (document)
- uid: "<the auth {uid} field>"
鉴于此反向映射,可以编写安全规则来强制用户名尚未被使用:
match /users/{userId} {
allow read: if true;
allow create, update: if
request.auth.uid == userId &&
request.resource.data.name is string &&
request.resource.data.name.size() >= 3 &&
get(/PATH/usernames/$(request.resource.data.name)).data.uid == userId;
}
并强制用户首先创建用户名文档:
match /usernames/{name} {
allow read: if true;
allow create: if
request.resource.data.size() == 1 &&
request.resource.data.uid is string &&
request.resource.data.uid == request.auth.uid;
}
我相信解决方案已经成功一半了。然而,仍有一些未解决的问题。
剩余问题/疑问
这个实现已经相当复杂,但它甚至没有解决用户想要的问题更改他们的用户名(需要记录删除或者更新规则等)
另一个问题是,没有什么可以阻止用户在usernames
收集,有效地抢夺所有好的用户名来破坏系统。
那么对于问题:
- 是否有更简单的解决方案来强制执行唯一用户名?
- 怎样才能发送垃圾邮件
usernames
收集被阻止?
- 如何使用户名检查不区分大小写?
我也尝试强制执行users
, 和另外一个exists()
/usernames 集合的规则,然后提交批量写入操作,但是,这似乎不起作用(“权限缺失或不足“ 错误)。
另请注意:我已经看到了带有客户端检查的解决方案。但这些都是不安全的。任何恶意客户端都可以修改代码并忽略检查。