在Google App Engine中生成唯一且不透明的用户ID

9
我正在开发一款应用程序,允许已注册用户创建或上传内容,并允许匿名用户查看该内容并浏览已注册用户的页面以找到该内容 - 这与 Flickr 等网站允许人们浏览其用户页面的方式非常相似。
为此,我需要一种方法来识别匿名 HTTP GET 请求中的用户。用户应该能够输入 http://myapplication.com/browse/<userid>/<contentid> 并跳转到正确的页面 - 用户 ID 应该是唯一的,但出于隐私原因,不能使用用户的电子邮件地址。
通过 Google App Engine,我可以获取与用户关联的电子邮件地址,但正如我所说,我不想使用它。当用户注册时,我可以要求他们选择一个唯一的用户名,但如果可能的话,我想使其成为可选项,以便注册过程尽可能简短。
另一种选择是在注册过程中生成一些随机 cookie(GUID?),然后使用它,但我没有想到明显的方法来保证这样的 cookie 的唯一性而不需要查询数据库。
是否有一种方法,可以在给定 App Engine 用户对象的情况下,获取可用于此方式的唯一标识符?
我正在寻找 Python 解决方案 - 我忘记了 GAE 现在也支持 Java。尽管如此,我期望技术在语言上无论如何都是相似的。
3个回答

7

您的时机非常完美:就在昨天,SDK发布了一个新版本,支持唯一、永久的用户ID。它们符合您所指定的所有标准。


如果当前用户未登录,则Users构造函数会引发UserNotFoundError错误。也就是说,它需要Google登录。然而,我认为使用Google登录机制比自己开发更好,特别是对于用户的期望。 - Mark
1
然而,我认为user_id可能是全球唯一的,这将不是一个好现象。 - Mark
这听起来正是我需要的。我确实使用Google登录,而一个全球唯一的用户ID实际上是必需的。太完美了。 - Ori Pessach
1
所以你不想追踪非Google用户?所谓世界独一无二,是指在其他网站上也是如此。如果user_id确实是世界独一无二的(我还没有测试过),你应该考虑到人们可能会将你的用户与他们的电子邮件匹配起来。 - Mark
哦,我明白你说的通过用户ID跟踪用户的意思了。我得考虑一下这个问题的影响。不需要追踪非Google用户。发布内容的人必须登录,因此必须是Google用户,但是为了访问网站并阅读内容,我只需要一种唯一的识别URL中内容的方法,使用user_id()就可以了。 - Ori Pessach
大家好,Nick、Ori和其他人。你们有没有成功地从User对象中生成user_id(),而不是通过users.get_current_user()调用?当我这样做时,user_id()返回None。如果你们有什么建议,我会很感激在我的问题页面上得到一些反馈:https://dev59.com/LXRA5IYBdhLWcg3wzhbZ谢谢。 - JasonSmith

3

我认为你应该区分两种类型的用户:

1)通过Google帐户登录或已使用非Google电子邮件地址在您的网站上注册的用户

2)第一次打开您的网站并且没有以任何方式登录的用户

对于第二种情况,我看不到其他方法,只能生成一些随机字符串(例如通过uuid.uuid4()或来自此用户的会话cookie键),因为匿名用户不携带任何唯一信息。

对于已登录的用户,您已经拥有唯一标识符--他们的电子邮件地址。我同意您的隐私问题--您不应将其用作标识符。相反,如何生成一个“看起来”随机但实际上是从电子邮件地址生成的字符串?哈希函数非常适合此目的。例如:

>>> import hashlib

>>> email = 'user@host.com'
>>> salt = 'SomeLongStringThatWillBeAppendedToEachEmail'

>>> key = hashlib.sha1('%s$%s' % (email, salt)).hexdigest()
>>> print key
f6cd3459f9a39c97635c652884b3e328f05be0f7

由于hashlib.sha1不是随机函数,因此对于给定的数据始终返回相同的结果,但已被证明在实践中是不可逆的。这样,您可以安全地在网站上显示散列密钥,并且不会泄露用户的电子邮件地址。此外,您可以放心地假设两个不同电子邮件的散列值将不相同(虽然可能会相同,但发生的概率非常小)。有关散列函数的更多信息,请参阅维基百科条目

我考虑过哈希,但由于可能发生冲突(虽然很少见,但一个健壮的程序应该检查它),所以它对我来说并没有太大的帮助。我仍然需要与数据库进行往返,此时我不妨只生成一个随机ID并检查它。这正是我想避免的。至于未经身份验证的用户,他们无法生成内容,因此这不是问题。 - Ori Pessach

1
你是指会话cookie吗?
试试http://code.google.com/p/gaeutilities/

DzinX 所说的没错。创建一个不需要数据库往返就能进行身份验证的不透明密钥的唯一方式是使用加密或密码哈希。

为用户提供一个随机数,并使用私钥进行散列或加密。您仍然会面临(微小的)冲突风险,但可以通过在密钥创建时触及数据库来避免这种情况,在冲突的情况下更改随机数。确保随机数是密码学的,并添加一个长的服务器端随机数以防止选择明文攻击。

最终您将得到一个类似于 Google Docs 密钥的令牌,基本上是证明用户已经过身份验证的签名,可以在不接触数据库的情况下验证。

但是,考虑到GAE的定价和bigtable的速度,如果您真的无法使用Google自己的身份验证,则最好使用会话ID。


不,我不是指会话cookie。GAE已经提供了这个功能来跟踪已登录的用户。我的问题特别涉及匿名用户及其与关联注册用户的内容交互。 - Ori Pessach
我的建议是对于未登录的用户使用gaeutilities。 - Mark
非登录用户以完全无状态的方式与应用程序交互,因此这并不适用。感谢您的指引,看起来是一个方便的库。 - Ori Pessach
我非常喜欢这个建议。实际上,如果 Google 没有刚发布带有唯一永久用户标识符的新 SDK,那么这基本上就是我要做的事情。唯一的问题是,即使你检查数据存储库,也很难(甚至可能不可能)避免冲突 - 如果两个用户同时注册并且他们最终使用相同的哈希值,那么在你检查数据和保存数据之间发生碰撞的可能性非常小。 - Ori Pessach
你可以通过事务 (http://code.google.com/appengine/docs/python/datastore/transactions.html) 或给哈希值添加唯一性约束(作为主键)来解决这个问题。如果每次创建随机密钥并查找它们,也会遇到同样的问题。但是,如果使用 Google 的身份验证是前提条件,最好充分利用它! - Mark
1
谷歌实现事务的方式是我说这很难做的原因。:) 我在应用程序的其他地方使用事务,并且对它们可以做什么有严格的限制。例如,不允许查询,这似乎限制了它们的适用性,除非我错过了一些聪明的东西。我也不确定关键字的唯一性 - 我还不太熟悉所有数据存储的怪癖。 - Ori Pessach

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接