ROR - 为数据库ID生成字母数字字符串

时间:2012-03-07 11:39:58

标签: ruby-on-rails-3 encoding hash unique

在我们的数据库中,每个Person都有一个ID,即生成数据库的自动递增整数。现在,我们想要生成一个更加用户友好的字母数字ID,可以公开曝光。像护照号码一样的东西。我们显然不希望将DB ID公开给用户。出于这个问题的目的,我将调用我们需要生成的内容UID

注意:UID并不是要替换数据库ID。您可以将UID视为数据库ID的更漂亮的版本,我们可以将其提供给用户。

  • 我想知道这个UID是否可以是数据库ID的功能。也就是说,我们应该能够为给定的DB ID重新生成相同的UID。
  • 显然,除了DB ID之外,该函数还会使用“salt”或密钥。
  • UID不应该是顺序的。也就是说,两个相邻的DB ID应该生成视觉上看起来不同的UID。
  • UID不是严格要求不可逆转的。也就是说,如果有人研究UID几天并且能够进行逆向工程并找到数据库ID,那也没关系。我认为它不会给我们带来任何伤害。
  • UID应仅包含A-Z(仅大写)和0-9。没有其他的。它不应包含可与其他字母或数字混淆的字符,如0和O,l和1等。我猜Crockford的Base32编码可以解决这个问题。
  • 无论数据库ID的大小如何,UID都应该是固定长度(10个字符)。我们可以用一些常量字符串填充UID,使其达到所需的固定长度。数据库ID可以增长到任何大小。因此,算法不应该有任何这样的输入限制。

我认为解决这个问题的方法是:

第1步:哈希。

我已经阅读了以下哈希函数:

哈希返回一个长字符串。我读了here关于XOR折叠的东西,把字符串缩短了。但我找不到太多关于此的信息。

第2步:编码。

我读到了以下编码方法:

  • Crockford Base 32编码
  • Z-Base32
  • Base36

我猜测编码的输出将是我正在寻找的UID字符串。

第3步:解决碰撞问题。

  • 为了解决冲突问题,我想知道是否可以在生成UID时生成随机密钥,并在函数中使用此随机密钥。
  • 我可以将这个随机密钥存储在一列中,以便我们知道用于生成该特定UID的密钥。
  • 在将新生成的UID插入表中之前,我会检查唯一性,如果检查失败,我可以生成一个新的随机密钥并使用它来生成新的UID。可以重复此步骤,直到找到特定数据库ID的唯一UID。

我希望得到一些关于我是否正确行以及如何实际实现这一目标的专家建议。

我将在 Ruby On Rails 应用程序中实现此功能。因此,请在您的建议中考虑到这一点。

感谢。

更新

评论和回答让我重新思考并质疑我的一个要求:我们需要在分配一次后为用户重新生成UID。我想我只是想保证安全,在我们丢失用户的UID的情况下,如果它是用户现有属性的函数,我们将能够恢复它。但我想我们可以通过使用备份解决这个问题。

因此,如果我删除了该要求,则UID基本上变成一个完全随机的10个字符的字母数字字符串。我正在添加一个包含我提议的实施计划的答案。如果其他人有更好的计划,我会将其标记为答案。

2 个答案:

答案 0 :(得分:2)

正如我在问题更新中所提到的,我认为我们要做的是:

  • 预生成足够多的随机且唯一的十个字符的字母数字字符串。没有散​​列或编码。
  • 以随机顺序将它们存储在表格中。
  • 创建用户时,请选择第一个这些字符串并将其分配给用户。
  • 在将ID分配给用户后,从ID池中删除此选中的ID。
  • 当池减少到较低的数量时,显然需要使用新的字符串补充池,并进行唯一性检查。这可以在由观察员发起的延迟工作中完成。
  • 预生成的原因是我们将所有昂贵的唯一性检查卸载到一次性预生成操作。
  • 从新池中为新用户选择ID时,保证唯一性。因此,创建用户(非常频繁)的操作变得很快。

答案 1 :(得分:0)

db_id.chr能为你效力吗?它将采用整数并从中生成一个字符串。然后,您可以附加他们的首字母或姓氏或其他任何内容。例如:

user = {:id => 123456, :f_name => "Scott", :l_name => "Shea"}
(user.id.to_s.split(//).map {|x| (x.to_i + 64).chr}).join.downcase + user.l_name.downcase

#result = "abcdefshea"