生成长度为7个字符的唯一随机字母数字字符

时间:2010-05-09 22:57:41

标签: php mysql passwords unique

它不一定是有意义的词 - 更像是随机密码生成,但问题是 - 它们应该是唯一的。我会将它用于某种包/产品代码。哪种方法最好? :)

15 个答案:

答案 0 :(得分:6)

通常不可能生成具有唯一和随机元素的序列:显然,该算法必须考虑序列中先前生成的元素,因此下一个元素实际上不是随机的。

因此,您最好的选择是检测碰撞并重试(在您的特定情况下可能会非常昂贵)。

如果你只受到7个字符的限制,那么上面你可以做的并不多:

$allowed_chars = 'abcdefghijklmnopqrstuvwxz';
$allowed_count = strlen($allowed_chars);
$password = null;
$password_length = 7;

while($password === null || already_exists($password)) {
    $password = '';
    for($i = 0; $i < $password_length; ++$i) {
        $password .= $allowed_chars{mt_rand(0, $allowed_count - 1)};
    }
}

这最终会给你一个新密码。

然而,在我遇到的类似情况下,我通常会选择一个更大的密码大小,这也恰好是流行哈希函数(例如md5)的十六进制表示的大小。然后,您可以使自己更容易,更不容易出错:

$password = time(); // even better if you have some other "random" input to use here

do {
    $password = md5(time().$password);
}
while (already_exists($password));

这还具有序列空间更大的附加优点,因此碰撞将更少。您可以根据将来生成的密码的预期数量来选择哈希函数的大小,以“保证”低冲突概率,从而减少对可能昂贵的already_exists函数的调用。

答案 1 :(得分:1)

这是一种没有哈希或循环的方法:

$password = sprintf(
    "%04s%03s",
    base_convert(mt_rand(0, pow(36, 4) - 1), 10, 36),
    base_convert(mt_rand(0, pow(36, 3) - 1), 10, 36)
);

正如其他一些人所提到的,确保独特性更复杂,应该是不必要的。最简单的方法是在最后添加额外的字符,随着生成的每个密码递增。

答案 2 :(得分:1)

这是一个看似随机的东西,应该是唯一的,并且有7个字符可供使用时间:

echo base_convert(intval(microtime(true) * 10000), 10, 36);

或者更多随机性和更少唯一性 1000 10000 每秒):< / p>

echo base_convert(mt_rand(1, 9) . intval(microtime(true) * 1000), 10, 36);

或(每秒 100 10000 之间的唯一性) - 这可能是最佳选择:

echo base_convert(mt_rand(10, 99) . intval(microtime(true) * 100), 10, 36);

或(每秒 10 10000 之间的唯一性):

echo base_convert(mt_rand(100, 999) . intval(microtime(true) * 10), 10, 36);

你明白了。

答案 3 :(得分:1)

具有7个字符的随机字母数字(基数36 = 0..9 + a..z)值必须在217678233678364164095之间具有基数10表示,以下片段证明了这一点:

var_dump(base_convert('1000000', 36, 10));                   //  2176782336
var_dump(base_convert('zzzzzzz', 36, 10));                   // 78364164095

为了使它独一无二,我们必须依赖非重复因子,显而易见的选择是time()

var_dump(time());                                            //  1273508728
var_dump(microtime(true));                                   //  1273508728.2883

如果我们只想确保每秒1个唯一代码的最小唯一性因子,我们可以这样做:

var_dump(base_convert(time() * 2, 10, 36));                  // 164ff8w
var_dump(base_convert(time() * 2 + 1, 10, 36));              // 164ff8x
var_dump(base_convert(time() * 2 + 2, 10, 36));              // 164ff8y
var_dump(base_convert(time() * 2 + 3, 10, 36));              // 164ff8z

您会注意到这些代码不是随机的,您还会注意到time()1273508728)小于2176782336(7的最小基数10代表) char code),这就是我做time() * 2的原因。

现在让我们做一些日期数学运算,以便在遵守旧版本PHP(< 5.0?)的整数限制的同时添加随机性并增加唯一性因子:

var_dump(1 * 60 * 60);                                       //       3600
var_dump(1 * 60 * 60 * 24);                                  //      86400
var_dump(1 * 60 * 60 * 24 * 366);                            //   31622400
var_dump(1 * 60 * 60 * 24 * 366 * 10);                       //  316224000
var_dump(1 * 60 * 60 * 24 * 366 * 20);                       //  632448000
var_dump(1 * 60 * 60 * 24 * 366 * 30);                       //  948672000
var_dump(1 * 60 * 60 * 24 * 366 * 31);                       //  980294400
var_dump(PHP_INT_MAX);                                       // 2147483647

关于PHP_INT_MAX我不确定最近版本的PHP中究竟发生了什么变化,因为以下内容在PHP 5.3.1中有明确的效果,也许有人可以对此有所了解:< / p>

var_dump(base_convert(PHP_INT_MAX, 10, 36));                 // zik0zj
var_dump(base_convert(PHP_INT_MAX + 1, 10, 36));             // zik0zk
var_dump(base_convert(PHP_INT_MAX + 2, 10, 36));             // zik0zl
var_dump(base_convert(PHP_INT_MAX * 2, 10, 36));             // 1z141z2
var_dump(base_convert(PHP_INT_MAX * 2 + 1, 10, 36));         // 1z141z3
var_dump(base_convert(PHP_INT_MAX * 2 + 2, 10, 36));         // 1z141z4

我在这里理性化了,我很无聊,我很无聊,所以我会很快完成。我们几乎可以使用整个基本36个字符集并安全生成顺序代码,其最小保证唯一性因子为每秒1个唯一代码for 3.16887646 years ,使用此代码:< / p>

base_convert(mt_rand(22, 782) . substr(time(), 2), 10, 36);

我刚刚意识到由于mt_rand()的第一个参数,上面的内容有时会返回重复值,为了产生独特的结果,我们需要稍微限制一下我们的36个charset:

base_convert(mt_rand(122, 782) . substr(time(), 2), 10, 36);

请注意,上述值仍然是连续的,为了使它们看起来随机,我们可以使用microtime(),但我们只能确保每秒10个代码的唯一性因子for 3.8 months

base_convert(mt_rand(122, 782) . substr(number_format(microtime(true), 1, '', ''), 3), 10, 36);

事实证明这比我最初的解决困难,因为有很多限制:

  • 使用整个基地36 charset
  • 生成随机代码
  • 每秒唯一性因子与唯一性的持久性之间的权衡
  • PHP整数限制

如果我们可以忽略上述任何一项,那将会容易得多,我确信这可以进一步优化,但就像我说的那样:这让我很无聊。也许有人想在我离开的地方选择这个。 =)我很饿! = S

答案 4 :(得分:0)

鉴于你在这里提到密码,我认为你需要一种安全的方法(即:某人不应该根据知道任何其他密码来猜测别人的密码)。您可以使用以下内容:

  1. 确定主密码,例如“MasterPassword”
  2. 对于生成的每个密码,请附加一个随机或顺序nonce,例如“MasterPassword1”,“MasterPassword2”。
  3. 对其执行加密哈希(SHA,MD5等)并将哈希值转换为十六进制表示形式,例如“ce7f181a44a4a5b7e43fe2b9a0b1f0c1”。
  4. 根据需要截断多少个字符 - 可能是你指出的七个字符:“ce7f181”。
  5. 检查之前是否已分配。如果没有,请将其作为密码返回。否则,请从2开始重复。
  6. 如果安全性不是问题,步骤1和2本身就足够了。如果安全是一个问题,那么除了你自己之外,没有人知道“MasterPassword”的价值是至关重要的。

答案 5 :(得分:0)

以下是我将如何解决这个问题:

考虑7个字符可以是26个字母(abc..z)中的一个,或10个数字(01 ... 9)。这使得36个可能的角色。

每次应用程序生成新代码时,都要增加一个全局变量。您可以使用“Hexatridecimal”转换器将此唯一编号转换为唯一字符串,并添加填充字符以构成字符串的其余部分。

看一下这个链接。我觉得这个家伙和你有同样的问题: http://www.codemaxima.com/2010/04/the-hexatridecimal-numbering-system/

答案 6 :(得分:0)

$random = substr(hash('md5',openssl_random_pseudo_bytes(32)),0,7);

答案 7 :(得分:0)

+1给@Michael Haren的评论。如果您网站上的密码不应具有唯一的约束。

如果我尝试使用给定的密码,并且我收到一个错误,我无法使用它,因为它已经在使用中,那么我知道系统上的某个用户有该密码。如果有1000个用户,我只需要尝试最多1000个其他帐户才能找到拥有该密码的用户。

没有真正回答你的问题,但不仅仅是评论。所以我正在标记这个CW。

答案 8 :(得分:0)

md5( microtime() );

答案 9 :(得分:0)

Galen的答案只允许在密码中使用每个字符。该字符串中的信息不多。一个简单的改变:

$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$passwordlength = 7;
for ($x = 1; $x <= $passwordlength; $x++) {
  $charlist .= $chars;
}
$temp_pw = substr( str_shuffle( $charlist ), 0, $passwordlength );

答案 10 :(得分:0)

这是我最喜欢的方式。

$pretrimmedrandom = md5(uniqid(mt_rand(),true));
$trimmed =  substr($pretrimmedrandom ,0,7);

uniqid使用当前时间生成一个非常独特的随机字符串。结果看起来像“3f456yg”。

答案 11 :(得分:0)

substr(str_shuffle(md5(microtime())),rand(0,21),7);

答案 12 :(得分:0)

试试这个

echo $unique_key = substr(md5(rand(0, 1000000)), 0, 5);

它将给出长度为5的字符串。

答案 13 :(得分:-1)

使用Kohana文本,

http://docs.kohanaphp.com/helpers/text

例如,

   $prod_id = text::random('alpha', 7);

如果您不想使用框架,只需复制代码即可。你会在那里找到很多好东西。

答案 14 :(得分:-1)

这是一个非常简单的方法

$chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
$temp_pw = substr( str_shuffle( $chars ), 0, 7 );
if ( check_unique( $temp_pw ) ) {
    $pw = $temp_pw;
}

您必须实现自己的check_unique函数。那部分应该很容易。