【问题标题】:ROR - Generate an alpha-numeric string for a DB IDROR - 为 DB ID 生成一个字母数字字符串
【发布时间】:2012-03-09 22:08:22
【问题描述】:

在我们的数据库中,每个Person 都有一个 ID,它是数据库生成的自动递增整数。现在,我们想要生成一个更加用户友好的字母数字 ID,它可以公开。护照号码之类的。我们显然不想将 DB ID 暴露给用户。出于这个问题的目的,我将把我们需要生成的东西称为UID

注意:UID 不能代替 DB ID。您可以将 UID 视为 DB ID 的更漂亮版本,我们可以将其提供给用户。

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

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

第 1 步:散列。

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

哈希返回一个长字符串。我阅读了here 的内容,该内容称为 XOR 折叠,可以将字符串缩短到更短的长度。但我找不到太多相关信息。

第 2 步:编码。

我了解到以下编码方法:

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

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

第 3 步:解决碰撞问题。

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

我很想就我是否走正确的路线以及如何实际实施这一点获得一些专家建议。

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

谢谢。

更新

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

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

【问题讨论】:

  • 创建一个基于年、月、日(如果需要,甚至是时间)的数字并跟进 YYYYMMDD/NN?其中 NN 也可以是字母/数字
  • 您是否建议我只使用时间戳 + 字母数字常量作为 UID?这对我来说有点太明显了。我不希望用户能够如此轻易地猜出 ID 背后的逻辑。
  • 好吧,不清楚后面有什么信息(假设它是一个 URL)。那么 SO 的做法呢?编号 + 名称 (623581/anjan),这个组合很难猜到(并非不可能)。
  • 正如我在问题中提到的,这个 UID 类似于护照号码。我们在数据库中有数以万计的用户,我显然不想在 UID 中使用名称。每个用户都应该被分配这个 UID,这是将给他们的,用来标识他们自己。这些用户实际上是学校在我们数据库中注册的学生,因此学生不会像传统注册系统那样选择唯一的“用户名”。我们必须自动分配一个唯一的 ID。我开始倾向于预先生成 10 个字母数字字符的所有可能组合并随机选择它们。
  • 我确实读过你的问题 ;-) 但有时跳出框框思考会在这件事上产生不同的影响。

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


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    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"
    

    【讨论】:

    • 感谢您的想法。我不想使用名称,因为它可以由用户更改。此外:(123456.to_s.split(//).map {|x| (x.to_i + 64).chr}).join.downcase(123457.to_s.split(//).map {|x| (x.to_i + 64).chr}).join.downcase 导致 abcdefabcdeg。这彼此太相似了;顺序的。正如我在问题中所说:两个相邻的 DB ID 应该生成外观不同的 UID。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-09
    • 2011-08-18
    • 2016-05-09
    • 1970-01-01
    • 2017-04-05
    相关资源
    最近更新 更多