5

我需要一种有效的方法来获取随机唯一的字符串,重复的机会为零(或可忽略不计)。

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

这是我到目前为止所拥有的:

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

5 回答 5

8

最近对 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. 如果您将时间(以秒为单位)作为整数并对其进行哈希处理,那么您最终会得到非常明确且容易猜到的盐

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


1:它适用于作曲家!

于 2013-02-03T16:10:12.080 回答
4

这就是我有时正在做的方式......

// 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。

于 2013-02-03T16:13:36.203 回答
2

你可以检查我自己的问题的答案。 如何使用实际订单 ID 生成唯一订单 ID(仅向用户展示)?

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

关于您的解决方案,有可能发生碰撞,但水平很低。

于 2013-02-03T16:10:15.667 回答
1

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

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

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

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

为什么不直接使用该time()功能。

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

我希望这有帮助

于 2014-03-12T08:39:02.217 回答