我需要一种有效的方法来获取既随机又唯一的字符串,并且有零(或可以忽略的)重复的几率。
我需要使用 [0-9A-z]
范围内的字符。
这是我目前拥有的:
substr(sha1(mt_rand().uniqid()),0,22);
我知道这实际上是在谈论bcrypt和密码加盐,现在我可以向阅读本文的人们指出他们应该使用的函数,而不是手动编写自己的加盐系统。
使用password_hash($input, PASSWORD_DEFAULT);
生成一个适合插入数据库的哈希值。这将为您获取salt。
插入:
$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 / [number of possible letters/numbers] ** [length]
对于一个22个字符的字符串来说,这些可能性非常小(嗯,并不是完全不可能,但可以忽略)
1 / (22 ** 60) = 1 / (3.51043 x 10**80)
看到了吗?微小的。
如果您需要一个真正随机的字符串(注意:这些字符串只是映射为字母的数字串),那么您可能有点运气不佳。
你要找的是CSPRNG(加密安全伪随机数生成器)。 不需要唯一性。
正如@Guarav在他的回答中指出的那样,您可以使用时间戳作为种子,然后进行哈希处理。 如果这是128位时间戳,则称为UUID(唯一通用标识符),它是可预测的,并且由于以下原因可能是不好的:
尽管如此,通过足够的准确性,您仍然可以使用时间戳作为唯一salt。不是 随机(除非您将其用作随机种子并将其转换为基数10,但这仍然是一个坏主意)。如果您可以计算小于纳秒级的时间并想将其用作唯一ID,则考虑这一点。 PHP无法快速处理以给出两个相撞的亚纳秒ID1(但这并不意味着您不应该验证!)
1:它适用于composer!
这是我做某事的方式...
// 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
sha1
。这是一种返回任何给定输入的一致输出的方法。它不会影响重复的可能性。substr
限制了字符串长度为22个字符,因此实际上稍微增加了重复的可能性。使用我在前一个项目中提到的内容,将随机数转换为少于22个字符的字符串,无需截断。sha1
函数返回一个原始值来消除对substr
的需要,该函数仅有20个字符。为什么不直接使用time()
函数呢?
我需要做类似的事情,一个保持唯一ID的解决方案,最终我采用了使用PHP函数time()
的解决方案,像这样$reference_number = 'BFF-' . time();
你可以将BFF更改为对你的业务逻辑更有意义的内容。这样,我就不必担心新生成的ID是否已被占用,因为时间总是唯一的。
希望这能帮到你。