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

PHP: How to generate a random, unique, alphanumeric string?

如何使用数字和字母生成用于验证链接的随机、唯一字符串?就像你在网站上创建一个帐户,它会给你发送一封带有链接的电子邮件,你必须点击链接来验证你的帐户……是的……其中之一。

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

更新:刚刚记起了uniqid()。它是一个PHP函数,基于当前时间(以微秒为单位)生成一个唯一的标识符。我想我会用的。


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

快速、简短和切题的答案是:

1
bin2hex(openssl_random_pseudo_bytes($bytes))

它将生成一个长度为$bytes*2的随机字母数字字符字符串。不幸的是,它只有一个[a-f][0-9]的字母表,但是它起作用了。下面是我能做的最强大的函数,它满足标准(这是Erik答案的一个实现版本)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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;
}

crypto_rand_secure($min, $max)作为rand()mt_rand的替代品。它使用openssl_random_伪字节来帮助创建一个介于$min和$max之间的随机数。

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

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

编辑(php7):随着php7的发布,标准库现在有了两个新功能,可以替换/改进/简化上面的加密和安全功能。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

例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
function getToken($length){
     $token ="";
     $codeAlphabet ="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     $codeAlphabet.="abcdefghijklmnopqrstuvwxyz";
     $codeAlphabet.="0123456789";
     $max = strlen($codeAlphabet); // edited

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

    return $token;
}


Security Notice: This solution should not be used in situations where the quality of your randomness can affect the security of an application. In particular, rand() and uniqid() are not cryptographically secure random number generators. See Scott's answer for a secure alternative.

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

md5(uniqid(rand(), true))

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

1
md5(uniqid($your_user_login, true))


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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
<?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);
    }
}

。用法

1
2
3
4
5
6
7
8
9
10
11
12
13
<?php

use Utils
andomStringGenerator;

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

// Set token length.
$tokenLength = 32;

// Call method to generate random string.
$token = $generator->generate($tokenLength);

号自定义字母表

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

1
2
3
4
5
6
7
8
9
<?php

$customAlphabet = '0123456789ABCDEF';

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

// Change alphabet whenever needed.
$generator->setAlphabet($customAlphabet);

号这是输出样本

1
2
3
4
5
6
7
8
9
10
SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
duoRF6gAawNOEQRICnOUNYmStWmOpEgS
sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
eVywMdYXczuZmHaJ50nIVQjOidEVkVna
baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa

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


我迟到了,但我在这里提供了一些很好的研究数据,这些数据是基于Scott的答案提供的函数。所以我为这个5天的自动测试设置了一个数字海洋液滴,并将生成的唯一字符串存储在MySQL数据库中。

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

enter image description here

更新:我创建了一个简单的Heroku应用程序,使用这些函数将令牌作为JSON响应返回。可以在https://uniquestrings.herokupp.com/api/token上访问该应用程序?长度=15

我希望这有帮助。


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

1
2
3
4
5
6
7
8
9
10
11
12
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);

示例输出:

1
zsd16xzv3jsytnp87tk7ygv73k8zmr0ekh6ly7mxaeyeh46oe8


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

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

1
2
3
4
5
6
7
8
9
10
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 )
    );

}

//调用函数

1
$transationID = generate_uuid();

一些示例输出如下:

1
2
3
4
5
6
7
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

希望以后能帮助别人:)


我用这一行:

1
base64_encode(openssl_random_pseudo_bytes(3 * ($length >> 2)));

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


  • 使用生成随机数你最喜欢的随机数发电机
  • 乘并除得到一个与数字匹配的数字代码字母表中的字符数
  • 获取该索引处的项你的代码字母表。
  • 从1)开始重复,直到你有足够的长度希望
  • 例如(伪代码)

    1
    2
    3
    4
    int myInt = random(0, numcharacters)
    char[] codealphabet = 'ABCDEF12345'
    char random = codealphabet[i]
    repeat until long enough


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

    1
    $randomNum=substr(str_shuffle("0123456789abcdefghijklmnopqrstvwxyz"), 0, 11);

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     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);


    这里是您的终极唯一ID生成器。我做的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    <?php
    $d=date ("d");
    $m=date ("m");
    $y=date ("Y");
    $t=time();
    $dmt=$d+$m+$y+$t;    
    $ran= rand(0,10000000);
    $dmtran= $dmt+$ran;
    $un=  uniqid();
    $dmtun = $dmt.$un;
    $mdun = md5($dmtran.$un);
    $sort=substr($mdun, 16); // if you want sort length code.

    echo $mdun;
    ?>

    您可以根据自己的喜好将任何"var"回送给您的ID。但是$mdun更好,您可以将md5替换为sha1以获得更好的代码,但这将是非常长的时间,这可能是您不需要的。

    谢谢您。


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

    1
    2
    3
    <?php

    echo md5(microtime(true).mt_Rand());

    输出:

    1
    40a29479ec808ad4bcff288a48a25d5c

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


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    function random_string($length = 8) {
        $alphabets = range('A','Z');
        $numbers = range('0','9');
        $additional_characters = array('_','=');
        $final_array = array_merge($alphabets,$numbers,$additional_characters);
           while($length--) {
          $key = array_rand($final_array);

          $password .= $final_array[$key];
                            }
      if (preg_match('/[A-Za-z0-9]/', $password))
        {
         return $password;
        }else{
        return  random_string();
        }

     }


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

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

    • 第二个是将这些随机字节转换为可打印字符串

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

    1
    2
    3
    4
    5
    6
    7
    $length = 32;

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

    //PHP < 7
    $bytes= openssl_random_pseudo_bytes($length);

    然后要将这些随机字节转换为可打印字符串:

    您可以使用bin2hex:

    1
    $string = bin2hex($bytes);

    或base64_编码:

    1
    $string = base64_encode($bytes);

    但是,请注意,如果使用base64,则不控制字符串的长度。您可以使用bin2hex来实现这一点,使用32个字节将变成64个字符的字符串。但它只能在偶数串中这样工作。


    我用的是:

    1
    2
    md5(time() . rand());    
    // Creates something like 0c947c3b1047334f5bb8a3b7adc1d97b


    我喜欢在处理验证链接时使用哈希键。我建议使用microtime和散列,使用md5,因为没有理由使用相同的密钥,因为它是基于microtime散列的。

  • $key = md5(rand());
  • 江户十一〔一〕号

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    function random_code($length)
    {
      return substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, $length);
    }

    echo random_code(6);

    /*sample output
    * a7d9e8
    * 3klo93
    */


    在阅读了前面的例子之后,我想到了:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    protected static $nonce_length = 32;

    public static function getNonce()
    {
        $chars = array();
        for ($i = 0; $i < 10; $i++)
            $chars = array_merge($chars, range(0, 9), range('A', 'Z'));
        shuffle($chars);
        $start = mt_rand(0, count($chars) - self::$nonce_length);
        return substr(join('', $chars), $start, self::$nonce_length);
    }

    我复制了10倍的数组[0-9,a-z]并对元素进行无序排列,在我得到了一个随机的起始点substr()以使其更具"创造性":)你可以在数组中添加[a-z]和其他元素,复制多少,比我更有创意


    如果要在PHP中生成唯一的字符串,请尝试以下操作。

    在这个问题上,

    uniqid()—它将生成唯一的字符串。此函数以字符串形式返回基于时间戳的唯一标识符。

    mt_rand()—生成随机数。

    md5()—它将生成哈希字符串。


    斯科特,是的,你写得很好,解决方案也很好!谢谢。

    我还需要为每个用户生成唯一的API令牌。下面是我的方法,我使用了用户信息(userid和username):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public function generateUniqueToken($userid, $username){

            $rand = mt_rand(100,999);
        $md5 = md5($userid.'!(&^ 532567_465 ///'.$username);

        $md53 = substr($md5,0,3);
        $md5_remaining = substr($md5,3);

        $md5 = $md53. $rand. $userid. $md5_remaining;

        return $md5;
    }

    请看一下,如果有什么改进,请告诉我。谢谢


    以下是我在其中一个项目中使用的工具,它工作得很好,生成了一个独特的随机令牌:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    $timestampz=time();

    function generateRandomString($length = 60) {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, $charactersLength - 1)];
        }
        return $randomString;
    }


    $tokenparta = generateRandomString();


    $token = $timestampz*3 . $tokenparta;

    echo $token;

    请注意,我将时间戳乘以3,为用户可能想知道如何生成此令牌的人创建一个混乱;)

    希望有帮助:)


    我认为这是最好的方法。

    1
    str_shuffle(md5(rand(0,100000)))


    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    <?php
    function generateRandomString($length = 11) {
        $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
        $charactersLength = strlen($characters);
        $randomString = '';
        for ($i = 0; $i < $length; $i++) {
            $randomString .= $characters[rand(0, $charactersLength - 1)];
        }
        return $randomString;

    }

    ?>

    上面的函数将生成一个长度为11个字符的随机字符串。


    我们可以使用这两行代码来生成唯一的字符串,已经测试了大约10000000次迭代

    1
    2
      $sffledStr= str_shuffle('abscdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_-+');
        $uniqueString = md5(time().$sffledStr);

    我总是用这个函数生成一个自定义的随机字母数字字符串…希望能帮上忙。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    <?php
      function random_alphanumeric($length) {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ12345689';
        $my_string = '';
        for ($i = 0; $i < $length; $i++) {
          $pos = mt_rand(0, strlen($chars) -1);
          $my_string .= substr($chars, $pos, 1);
        }
        return $my_string;
      }
      echo random_alphanumeric(50); // 50 characters
    ?>

    例如:y1fypdjvbfcfk6gh9fdjpe6dciwjefv6mqgpjqafuijaysz86g

    如果要与另一个字符串进行比较以确保这是一个唯一的序列,可以使用此技巧…

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $string_1 = random_alphanumeric(50);
    $string_2 = random_alphanumeric(50);
    while ($string_1 == $string_2) {
      $string_1 = random_alphanumeric(50);
      $string_2 = random_alphanumeric(50);
      if ($string_1 != $string_2) {
         break;
      }
    }
    echo $string_1;
    echo"
    "
    ;
    echo $string_2;

    它生成两个唯一的字符串:

    qsbds4joovrffxyloagecyswvpcpmzao9pypwxsqpkeamyoi公司

    ti3ke1wfggtnxqvxtbnbhhvvapnaufgmvjechkujhbucb85pf

    希望这就是你要找的…


    我相信所有现存的想法的问题是,它们可能是独特的,但不是绝对独特的(正如达留斯瓦茨克在对洛雷特的答复中指出的)。我有一个真正独特的解决方案。它要求脚本具有某种内存。对我来说,这是一个SQL数据库。您也可以简单地在某个地方写入一个文件。有两种实现:

    第一种方法:有两个字段而不是提供唯一性的1。第一个字段是一个ID号,它不是随机的,但是唯一的(第一个ID是1,第二个是2…)。如果您使用的是SQL,只需使用auto_increment属性定义id字段。第二个字段不是唯一的,而是随机的。这可以用人们已经提到的任何其他技术生成。Scott的想法很好,但MD5很方便,可能在大多数情况下都足够好:

    1
    $random_token = md5($_SERVER['HTTP_USER_AGENT'] . time());

    第二种方法:基本上是相同的想法,但最初选择将要生成的最大数量的字符串。这可能是一个非常大的数字,比如万亿。然后做同样的事情,生成一个ID,但是零填充它,这样所有ID的位数都是相同的。然后将ID与随机字符串连接起来。对于大多数目的来说,它是足够随机的,但是ID部分将确保它也是唯一的。


    您可以使用此代码,希望对你有帮助。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    function rand_code($len)
    {
     $min_lenght= 0;
     $max_lenght = 100;
     $bigL ="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
     $smallL ="abcdefghijklmnopqrstuvwxyz";
     $number ="0123456789";
     $bigB = str_shuffle($bigL);
     $smallS = str_shuffle($smallL);
     $numberS = str_shuffle($number);
     $subA = substr($bigB,0,5);
     $subB = substr($bigB,6,5);
     $subC = substr($bigB,10,5);
     $subD = substr($smallS,0,5);
     $subE = substr($smallS,6,5);
     $subF = substr($smallS,10,5);
     $subG = substr($numberS,0,5);
     $subH = substr($numberS,6,5);
     $subI = substr($numberS,10,5);
     $RandCode1 = str_shuffle($subA.$subD.$subB.$subF.$subC.$subE);
     $RandCode2 = str_shuffle($RandCode1);
     $RandCode = $RandCode1.$RandCode2;
     if ($len>$min_lenght && $len<$max_lenght)
     {
     $CodeEX = substr($RandCode,0,$len);
     }
     else
     {
     $CodeEX = $RandCode;
     }
     return $CodeEX;
    }

    关于PHP中随机代码生成器的详细信息


    通过删除不必要的循环来简化上面的scotts代码,这会严重减慢速度,并且不会使其比只调用一次openssl_random_pseudo_字节更安全。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    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
     $rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
     return $min + $rnd%$range;
    }

    function getToken($length)
    {
     return bin2hex(openssl_random_pseudo_bytes($length)
    }