【问题标题】:Generating a random and unique string生成随机且唯一的字符串
【发布时间】:2013-02-03 16:03:24
【问题描述】:

我需要一种有效的方法来获取 randomunique 字符串,并且重复的可能性为零(或可忽略不计)。

我需要[0-9A-z] 范围内的字符。

这是我目前所拥有的:

substr(sha1(mt_rand().uniqid()),0,22);  

【问题讨论】:

    标签: php unique


    【解决方案1】:

    最近对 PHP 的更改

    既然我知道这实际上是在谈论 bcrypt 和密码加盐,现在我真的可以将阅读本文的人指向他们应该使用的功能,而不是手动滚动他们自己的加盐系统。

    使用password_hash($input, PASSWORD_DEFAULT); 生成适合插入数据库的散列。这将为您取盐。

    插入:

    $hash = password_hash($_POST["password"], PASSWORD_DEFAULT, ["cost" => 16]);
    DB::table("users")->insert(["username" => $user, "password" => $hash]);
    // or whatever database method you use to insert data
    

    验证:

    $hash = DB::table("users")->fetchByName($username)->select("password");
    $input = $_POST["password"];
    
    $verified = password_verify($input, $hash); // true if the password matches
    

    在 PHP 5.5 之前的版本中,使用 https://github.com/ircmaxell/password_compat 作为插件1


    当随机生成盐时,碰撞的几率是

    1 / [number of possible letters/numbers] ** [length]
    

    对于 22 个字符的字符串来说,这是不可能的低(嗯,不是 不可能,而是 可以忽略不计

    1 / (22 ** 60) = 1 / (3.51043 x 10**80)
    

    看到了吗? 很小


    数学谬误

    如果您需要一个真正随机的字符串(注意:这些字符串只是映射到字母的一行数字),那么您就有点不走运了。
    您正在寻找的是 CSPRNG(加密安全伪随机数生成器)。不需要唯一性。

    正如 @Guarav 在他的回答中指出的那样,您可以使用时间戳作为种子,然后对其进行哈希处理。这称为 UUID(唯一通用标识符,如果它是 128 位时间戳)是可预测的,并且由于多种原因可能会很糟糕:

    1. 获取此时间戳的准确度成为决定此盐的可预测性的决定因素。
    2. 如果您将时间(以秒为单位)作为整数并对其进行哈希处理,那么您最终会得到非常明确且容易猜到的盐

    尽管如此,在足够准确的情况下,您仍然可以将时间戳用作唯一盐。不是 random(除非您将其用作随机种子并将其基本转换为 base10,这仍然是一个坏主意)。如果您可以在纳秒以下计算时间并喜欢将其用作唯一 ID,请考虑这一点。 PHP 无法以足够快的速度处理以提供两个冲突的亚纳秒 ID1(但这并不意味着您不应该验证!)


    1:它适用于作曲家!

    【讨论】:

    • 困扰我的是,当 bcrypt 将盐与哈希一起存储时,我必须单独存储盐,但我无法查询所有行,从哈希中提取盐并检查上一个用法!这是徒劳的!如果成千上万的用户中有两个共享相同的盐,会不会有问题?
    • 如果您使用的是 bcrypt,您实际上并不需要 salts 才能真正唯一地用于密码散列。从纯数学的角度来看,两次使用相同的盐与使用不同的盐一样随机。此外,我链接的那个类在现实世界的登录系统中使用,并且击败了旧的(他们使用的是 unsalted sha1,shudder
    • 谢谢!我迫切希望找到一种方法来获得独特的盐,同时使用 bcrypt 而无需存储来检查它们!
    • 我会将其添加到答案中,以防它对未来的访问者有所帮助
    【解决方案2】:

    这就是我有时会做的事情......

    // chars
    $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()-+';
    
    // convert to array
    $arr = str_split($chars, 1);
    
    // shuffle the array
    shuffle($arr);
    
    // array to chars with 22 chars
    echo substr(implode('', $arr), 0, 22);
    

    输出

    xd*thKM$B#13^)9!QkD@gU
    
    ixXYL0GEHRf+SNn#gcJIq-
    
    $0LruRlgpjv1XS8xZq)hwY
    
    $G-MKXf@rI3hFwT4l9)j0u
    

    为确保它是唯一的,您可以随时签入您的数据库。万一重复生成KEY。

    【讨论】:

    • 有趣。我从没想过使用随机播放;我只是将字符串用作数组并根据数字随机挑选出字符。 +1。
    • @Madan Sapkota 您的方法与我使用的方法相似。但是,使用您的方法,每个字符只会出现一次,因此随机性要小得多。试试这个。它对我很有用:$pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; return substr(str_shuffle(str_repeat($pool, 22)), 0, 22);
    【解决方案3】:

    您可以检查我自己的问题的答案。 How to generate Unique Order Id (just to show touser) with actual Order Id?

    您可以使用 TimeStamp 代替任何随机字符串。

    关于你的解决方案,有碰撞的可能性,但它的水平很低。

    【讨论】:

      【解决方案4】:

      您不能保证这将始终返回一个唯一值,但它会显着降低风险。使用随机数生成算法时,总是有重复的可能性。

      为确保仅生成唯一值,您可以搜索以前生成的值并丢弃重复项。

      一些减少公式中重复机会的建议:

      • 不要使用sha1。这是一种为任何给定输入返回一个一致输出的方法。它不会影响重复的可能性。
      • 考虑将随机数转换为不同的基数,而不是 sha1(例如,基数为 36 的数字可以使用字符 0-9 和 A-Z,您可以将它们存储在数据库中或您正在执行的任何操作中)随机字符串输出)。但我真的不会在这里选择 36,也许是 256+?
      • 由于您使用substr 将字符串限制为22 个字符,因此您实际上稍微增加了重复的机会。使用我在上一个项目符号中所写的内容将随机数转换为不需要截断的少于 22 个字符的字符串。
      • 此外,您可以通过使用 sha1 函数返回一个 原始 值来消除对 substr 的需求,该值只有 20 个字符。
      • 或者,不要将字符串限制为 22 个字符(除非出于某种原因确实需要)。

      【讨论】:

        【解决方案5】:

        为什么不直接使用time() 函数。

        我需要做类似的事情,一个保持唯一 ID 的解决方案,我最终得到了一个使用 PHP 函数 time() 的解决方案,就像这样 $reference_number = 'BFF-' . time(); 你可以将 BFF 更改为对你的业务逻辑更有意义的东西.这样我就不必担心是否正在生成的新 id 之前被占用,因为时间总是唯一的

        希望对你有帮助

        【讨论】:

          猜你喜欢
          • 2012-11-21
          • 2010-10-18
          • 2013-05-20
          • 1970-01-01
          • 2011-08-18
          • 2017-02-21
          • 2013-03-05
          • 2017-06-06
          相关资源
          最近更新 更多