PHP:如何生成随机,唯一,字母数字字符串?

And*_*rew 359 php string random uniqueidentifier

如何使用数字和字母生成随机的唯一字符串以用于验证链接?就像您在网站上创建一个帐户一样,它会向您发送一封带有链接的电子邮件,您必须单击该链接才能验证您的帐户...是的......其中一个.

如何使用PHP生成其中一个?

更新:记住了uniqid().它是一个PHP函数,它根据当前时间(以微秒为单位)生成唯一标识符.我想我会用那个.

Sco*_*ott 475

我只是在研究如何解决同样的问题,但我也希望我的函数能够创建一个可用于密码检索的令牌.这意味着我需要限制令牌猜测的能力.因为uniqid是基于时间的,并且根据php.net"返回值与microtime()略有不同",uniqid不符合标准.PHP建议使用openssl_random_pseudo_bytes()生成加密安全令牌.

一个快速,简短和重点的答案是:

bin2hex(openssl_random_pseudo_bytes($bytes))
Run Code Online (Sandbox Code Playgroud)

这将生成一个随机字符串的字母数字字符长度= $ bytes*2.不幸的是,这只有一个字母表[a-f][0-9],但它的工作原理.


下面是我能做出的最强大的功能,满足标准(这是Erik答案的实现版本).

function crypto_rand_secure($min, $max)
{
    $range = $max - $min;
    if ($range < 1) return $min; // not so random...
    $log = ceil(log($range, 2));
    $bytes = (int) ($log / 8) + 1; // length in bytes
    $bits = (int) $log + 1; // length in bits
    $filter = (int) (1 << $bits) - 1; // set all lower bits to 1
    do {
        $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
        $rnd = $rnd & $filter; // discard irrelevant bits
    } while ($rnd > $range);
    return $min + $rnd;
}

function getToken($length)
{
    $token = "";
    $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
    $codeAlphabet.= "0123456789";
    $max = strlen($codeAlphabet); // edited

    for ($i=0; $i < $length; $i++) {
        $token .= $codeAlphabet[crypto_rand_secure(0, $max-1)];
    }

    return $token;
}
Run Code Online (Sandbox Code Playgroud)

crypto_rand_secure($min, $max)作为替代rand()或替代mt_rand.它使用openssl_random_pseudo_bytes来帮助创建$ min和$ max之间的随机数.

getToken($length)创建一个在令牌中使用的字母表,然后创建一个长度字符串$length.

编辑:我忽略了引用来源 - http://us1.php.net/manual/en/function.openssl-random-pseudo-bytes.php#104322

编辑(PHP7):随着PHP7的发布,标准库现在有两个新函数可以替换/改进/简化上面的crypto_rand_secure函数.random_bytes($length)random_int($min, $max)

http://php.net/manual/en/function.random-bytes.php

http://php.net/manual/en/function.random-int.php

例:

function getToken($length){
     $token = "";
     $codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     $codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
     $codeAlphabet.= "0123456789";
     $max = strlen($codeAlphabet);

    for ($i=0; $i < $length; $i++) {
        $token .= $codeAlphabet[random_int(0, $max-1)];
    }

    return $token;
}
Run Code Online (Sandbox Code Playgroud)

  • 嘿,我将优秀的代码与方便的Kohana Text :: random()方法功能相结合,并创建了这个要点:https://gist.github.com/raveren/5555297 (22认同)
  • 完善!我测试了10个字符作为长度.100000令牌,仍然没有重复! (9认同)
  • 我用这个过程发现它超级慢.它的长度为36,它在开发服务器上超时.长度为24,仍然需要大约30秒.不知道我做错了什么,但对我来说太慢了,我选择了另一个解决方案. (5认同)
  • 你也可以使用`base64_encode`而不是`bin2hex`来获得一个字母更大的字符串. (4认同)
  • 同意.明文密码存储很糟糕!(我使用blowfish.)但是在生成令牌之后,它允许令牌的持有者重置密码,因此令牌在有效时是密码等效的(并且被视为这样). (2认同)
  • 我想知道碰撞的统计数据是什么? (2认同)
  • 我可能会错过一些东西,但我不知道如何确保生成的令牌是唯一的。 (2认同)

小智 287

安全注意事项:在随机性质量会影响应用程序安全性的情况下,不应使用此解决方案.特别是,rand()并且uniqid()不是加密安全的随机数生成器.请参阅斯科特关于安全替代方案的答案.

如果你不需要它随着时间的推移绝对独特:

md5(uniqid(rand(), true))

否则(假设您已为用户确定了唯一的登录名):

md5(uniqid($your_user_login, true))
Run Code Online (Sandbox Code Playgroud)

  • 两种方法都不保证*唯一性 - md5函数的输入长度大于其输出的长度,并且根据http://en.wikipedia.org/wiki/Pigeonhole_principle,保证了冲突.另一方面,依靠哈希来实现"唯一"id的人口越多,发生至少一次碰撞的概率就越大(参见http://en.wikipedia.org/wiki/Birthday_problem).大多数解决方案的概率可能很小,但仍然存在. (87认同)
  • 这不是生成随机值的安全方法.见斯科特的答案. (12认同)
  • 对于即将到期的电子邮件确认,我认为一个人可能有一天确认别人的电子邮件的可能性微乎其微.但是,您可以在创建令牌后始终检查令牌是否已存在于数据库中,如果是这种情况,则只需选择一个新令牌.但是,您编写该代码片段所花费的时间可能会浪费,因为它很可能永远不会被运行. (4认同)
  • md5 可以有冲突,你刚刚破坏了 uniqid 的优势 (2认同)

Sla*_* II 89

面向对象版本的最高投票解决方案

我根据Scott的回答创建了一个面向对象的解决方案:

<?php

namespace Utils;

/**
 * Class RandomStringGenerator
 * @package Utils
 *
 * Solution taken from here:
 * http://stackoverflow.com/a/13733588/1056679
 */
class RandomStringGenerator
{
    /** @var string */
    protected $alphabet;

    /** @var int */
    protected $alphabetLength;


    /**
     * @param string $alphabet
     */
    public function __construct($alphabet = '')
    {
        if ('' !== $alphabet) {
            $this->setAlphabet($alphabet);
        } else {
            $this->setAlphabet(
                  implode(range('a', 'z'))
                . implode(range('A', 'Z'))
                . implode(range(0, 9))
            );
        }
    }

    /**
     * @param string $alphabet
     */
    public function setAlphabet($alphabet)
    {
        $this->alphabet = $alphabet;
        $this->alphabetLength = strlen($alphabet);
    }

    /**
     * @param int $length
     * @return string
     */
    public function generate($length)
    {
        $token = '';

        for ($i = 0; $i < $length; $i++) {
            $randomKey = $this->getRandomInteger(0, $this->alphabetLength);
            $token .= $this->alphabet[$randomKey];
        }

        return $token;
    }

    /**
     * @param int $min
     * @param int $max
     * @return int
     */
    protected function getRandomInteger($min, $max)
    {
        $range = ($max - $min);

        if ($range < 0) {
            // Not so random...
            return $min;
        }

        $log = log($range, 2);

        // Length in bytes.
        $bytes = (int) ($log / 8) + 1;

        // Length in bits.
        $bits = (int) $log + 1;

        // Set all lower bits to 1.
        $filter = (int) (1 << $bits) - 1;

        do {
            $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));

            // Discard irrelevant bits.
            $rnd = $rnd & $filter;

        } while ($rnd >= $range);

        return ($min + $rnd);
    }
}
Run Code Online (Sandbox Code Playgroud)

用法

<?php

use Utils\RandomStringGenerator;

// Create new instance of generator class.
$generator = new RandomStringGenerator;

// Set token length.
$tokenLength = 32;

// Call method to generate random string.
$token = $generator->generate($tokenLength);
Run Code Online (Sandbox Code Playgroud)

自定义字母

如果需要,您可以使用自定义字母.只需将带有受支持字符的字符串传递给构造函数或setter:

<?php

$customAlphabet = '0123456789ABCDEF';

// Set initial alphabet.
$generator = new RandomStringGenerator($customAlphabet);

// Change alphabet whenever needed.
$generator->setAlphabet($customAlphabet);
Run Code Online (Sandbox Code Playgroud)

这是输出样本

SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
duoRF6gAawNOEQRICnOUNYmStWmOpEgS
sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
eVywMdYXczuZmHaJ50nIVQjOidEVkVna
baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa
Run Code Online (Sandbox Code Playgroud)

我希望它能帮助别人.干杯!

  • 是的,很好,但是有点过头了,特别是考虑到它现在在 php 5.7 中已经被弃用了 (2认同)

Reh*_*mat 34

我迟到了,但我在这里根据斯科特答案提供的功能提供了一些很好的研究数据.因此,我为这个为期5天的自动测试设置了一个Digital Ocean Droplet,并将生成的唯一字符串存储在MySQL数据库中.

在此测试期间,我使用了5种不同的长度(5,10,15,20,50),每个长度插入了+/- 0.5百万条记录.在我的测试期间,只有长度为5的生成+/- 3K重复了50万,其余长度没有产生任何重复.所以我们可以说如果我们使用Scott的函数长度为15或更高,那么我们就可以生成高度可靠的唯一字符串.这是显示我的研究数据的表格:

在此输入图像描述

我希望这有帮助.


小智 31

此函数将使用数字和字母生成随机密钥:

function random_string($length) {
    $key = '';
    $keys = array_merge(range(0, 9), range('a', 'z'));

    for ($i = 0; $i < $length; $i++) {
        $key .= $keys[array_rand($keys)];
    }

    return $key;
}

echo random_string(50);
Run Code Online (Sandbox Code Playgroud)

示例输出:

zsd16xzv3jsytnp87tk7ygv73k8zmr0ekh6ly7mxaeyeh46oe8
Run Code Online (Sandbox Code Playgroud)

  • 过了一会儿你会得到相同的数字 (6认同)

Dev*_*per 31

您可以使用UUID(通用唯一标识符),它可以用于任何目的,从用户身份验证字符串到支付交易ID.

UUID是16个八位字节(128位)的数字.在其规范形式中,UUID由32个十六进制数字表示,显示在由连字符分隔的五个组中,形式为8-4-4-4-12,总共36个字符(32个字母数字字符和四个连字符).

function generate_uuid() {
    return sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
        mt_rand( 0, 0xffff ), mt_rand( 0, 0xffff ),
        mt_rand( 0, 0xffff ),
        mt_rand( 0, 0x0C2f ) | 0x4000,
        mt_rand( 0, 0x3fff ) | 0x8000,
        mt_rand( 0, 0x2Aff ), mt_rand( 0, 0xffD3 ), mt_rand( 0, 0xff4B )
    );

}
Run Code Online (Sandbox Code Playgroud)

//调用函数

$transationID = generate_uuid();
Run Code Online (Sandbox Code Playgroud)

一些示例输出将如下:

E302D66D-87E3-4450-8CB6-17531895BF14
22D288BC-7289-442B-BEEA-286777D559F2
51B4DE29-3B71-4FD2-9E6C-071703E1FF31
3777C8C6-9FF5-4C78-AAA2-08A47F555E81
54B91C72-2CF4-4501-A6E9-02A60DCBAE4C
60F75C7C-1AE3-417B-82C8-14D456542CD7
8DE0168D-01D3-4502-9E59-10D665CEBCB2
Run Code Online (Sandbox Code Playgroud)

希望它能帮助将来的某个人:)


Dud*_*ock 15

我用这个单线:

base64_encode(openssl_random_pseudo_bytes(3 * ($length >> 2)));
Run Code Online (Sandbox Code Playgroud)

其中length是所需字符串的长度(可被4整除,否则会向下舍入到最接近4的整数)

  • 它并不总是仅返回字母数字字符.它可以包含像`==`这样的字符 (6认同)

Ram*_*kar 9

使用下面的代码生成11个字符的随机数,或根据您的要求更改数字.

$randomNum=substr(str_shuffle("0123456789abcdefghijklmnopqrstvwxyz"), 0, 11);
Run Code Online (Sandbox Code Playgroud)

或者我们可以使用自定义函数来生成随机数

 function randomNumber($length){
     $numbers = range(0,9);
     shuffle($numbers);
     for($i = 0;$i < $length;$i++)
        $digits .= $numbers[$i];
     return $digits;
 }

 //generate random number
 $randomNum=randomNumber(11);
Run Code Online (Sandbox Code Playgroud)

  • 这永远不会生成字符串"aaaaaaaaaaa"或"aaaaabbbbbb".它只是选择字母表随机排列的子串.11个字符长的字符串只有V(11,35)= 288个可能的值.如果您希望字符串是唯一的,请不要使用它! (3认同)

Eri*_*oen 7

  1. 使用您最喜欢的随机数生成器生成随机数
  2. 将其相乘并除以得到与代码字母表中的字符数相匹配的数字
  3. 在代码字母表中获取该索引处的项目.
  4. 从1)重复,直到你有你想要的长度

例如(伪代码)

int myInt = random(0, numcharacters)
char[] codealphabet = 'ABCDEF12345'
char random = codealphabet[i]
repeat until long enough
Run Code Online (Sandbox Code Playgroud)


小智 7

对于真正随机的字符串,您可以使用

<?php

echo md5(microtime(true).mt_Rand());
Run Code Online (Sandbox Code Playgroud)

输出:

40a29479ec808ad4bcff288a48a25d5c
Run Code Online (Sandbox Code Playgroud)

因此,即使您尝试在完全相同的时间多次生成字符串,您也会得到不同的输出。


Arp*_* J. 6

这是一个简单的函数,允许您生成包含字母和数字(字母数字)的随机字符串。您还可以限制字符串长度。这些随机字符串可用于各种目的,包括:推荐代码、促销代码、优惠券代码。函数依赖于以下 PHP 函数: base_convert、sha1、uniqid、mt_rand

function random_code($length)
{
  return substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
}

echo random_code(6);

/*sample output
* a7d9e8
* 3klo93
*/
Run Code Online (Sandbox Code Playgroud)


Dyl*_*Kas 5

尝试生成随机密码时,您正在尝试:

  • 首先生成一组加密安全的随机字节

  • 其次是将这些随机字节转换为可打印的字符串

现在,有多种方法可以在 php 中生成随机字节,例如:

$length = 32;

//PHP 7+
$bytes= random_bytes($length);

//PHP < 7
$bytes= openssl_random_pseudo_bytes($length);
Run Code Online (Sandbox Code Playgroud)

然后你想把这些随机字节变成一个可打印的字符串:

您可以使用bin2hex

$string = bin2hex($bytes);
Run Code Online (Sandbox Code Playgroud)

base64_encode

$string = base64_encode($bytes);
Run Code Online (Sandbox Code Playgroud)

但是,请注意,如果使用 base64,则无法控制字符串的长度。您可以使用 bin2hex 来做到这一点,使用32 个字节将变成64 个字符的 string。但它只能在EVEN字符串中像这样工作。

所以基本上你可以这样做

$length = 32;

if(PHP_VERSION>=7){
    $bytes= random_bytes($length);
}else{
    $bytes= openssl_random_pseudo_bytes($length);
} 

$string = bin2hex($bytes);
Run Code Online (Sandbox Code Playgroud)