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

时间:2009-12-04 10:49:01

标签: php string random uniqueidentifier

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

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

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

31 个答案:

答案 0 :(得分:447)

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

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

bin2hex(openssl_random_pseudo_bytes($bytes))

将生成长度为= $ 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;
}

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); // edited

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

    return $token;
}

答案 1 :(得分:272)

  

安全通知:在随机性质量会影响应用程序安全性的情况下,不应使用此解决方案。特别是rand() and uniqid() are not cryptographically secure random number generators。有关安全的替代方案,请参阅Scott's answer

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

md5(uniqid(rand(), true))

否则(鉴于您已为用户确定了唯一的登录信息):

md5(uniqid($your_user_login, true))

答案 2 :(得分:86)

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

我已根据 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);
    }
}

用法

<?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);

自定义字母

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

<?php

$customAlphabet = '0123456789ABCDEF';

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

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

这里是输出样本

SRniGU2sRQb2K1ylXKnWwZr4HrtdRgrM
q1sRUjNq1K9rG905aneFzyD5IcqD4dlC
I0euIWffrURLKCCJZ5PQFcNUCto6cQfD
AKwPJMEM5ytgJyJyGqoD5FQwxv82YvMr
duoRF6gAawNOEQRICnOUNYmStWmOpEgS
sdHUkEn4565AJoTtkc8EqJ6cC4MLEHUx
eVywMdYXczuZmHaJ50nIVQjOidEVkVna
baJGt7cdLDbIxMctLsEBWgAw5BByP5V0
iqT0B2obq3oerbeXkDVLjZrrLheW4d8f
OUQYCny6tj2TYDlTuu1KsnUyaLkeObwa

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

答案 3 :(得分:31)

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

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

enter image description here

<强>更新: 我使用这些函数创建了一个简单的Heroku应用程序,它将令牌作为JSON响应返回。可以通过https://uniquestrings.herokuapp.com/api/token?length=15

访问该应用

我希望这会有所帮助。

答案 4 :(得分:30)

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

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);

示例输出:

zsd16xzv3jsytnp87tk7ygv73k8zmr0ekh6ly7mxaeyeh46oe8

答案 5 :(得分:25)

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

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

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 )
    );

}

//调用函数

$transationID = generate_uuid();

一些示例输出将如下:

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

希望将来帮助某人:)

答案 6 :(得分:13)

我使用这个单行:

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

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

答案 7 :(得分:7)

  1. 使用生成随机数 你最喜欢的随机数 发电机
  2. 乘以并除以它 得到一个与数字匹配的数字 代码字母表中的字符
  3. 获取该索引中的项目 你的代码字母表。
  4. 从1)重复,直到你有你的长度 想
  5. 例如(伪代码)

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

答案 8 :(得分:7)

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

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

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

 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);

答案 9 :(得分:4)

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

<?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'。但是$ mdun更好,你可以将md5替换为sha1以获得更好的代码,但这将是非常长的,你可能不需要。

谢谢。

答案 10 :(得分:3)

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

<?php

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

输出:

40a29479ec808ad4bcff288a48a25d5c

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

答案 11 :(得分:2)

以下是我使用的内容:

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

答案 12 :(得分:2)

尝试生成随机密码时,您尝试执行以下操作:

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

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

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

$length = 32;

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

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

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

您可以使用 bin2hex

$string = bin2hex($bytes);

base64_encode

$string = base64_encode($bytes);

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

答案 13 :(得分:2)

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();
    }

 }

答案 14 :(得分:1)

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

  1. $key = md5(rand());
  2. $key = md5(microtime());

答案 15 :(得分:1)

在阅读前面的例子后,我想出了这个:

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);
}

我将数组[0-9,A-Z]重复10次并对元素进行洗牌,之后我得到一个随机的起点,使substr()变得更具创造力&#39; :) 你可以将[a-z]和其他元素添加到数组中,重复或多或少,比我更有创意

答案 16 :(得分:1)

这个人被一杯水呛到...

$random= substr(str_shuffle("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ*.-_"), 0, 10);

简单。 这个随机字符串重复的可能性是 0,000000000000000000000000000001^70

答案 17 :(得分:1)

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

md5(uniqid().mt_rand());

在此

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

mt_rand()-生成随机数。

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

答案 18 :(得分:1)

这是一个简单的函数,可让您生成包含字母和数字(字母数字)的随机字符串。您还可以限制字符串长度。 这些随机字符串可用于各种目的,包括:推荐代码,促销代码,优惠券代码。 函数依赖于以下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
*/

答案 19 :(得分:0)

这里很有趣

public function randomStr($length = 16) {
    $string = '';
        
    while (($len = strlen($string)) < $length) {
        $size = $length - $len;
            
        $bytes = random_bytes($size);
            
        $string .= substr(str_replace(['/', '+', '='], '', base64_encode($bytes)), 0, $size);
    }
        
        return $string;
}

从 Laravel here 盗取了

答案 20 :(得分:0)

一种简单的解决方案是通过丢弃非字母数字字符将base 64转换为字母数字。

此代码使用random_bytes()获得加密安全的结果。

hamming_distance

答案 21 :(得分:0)

我总是使用此函数来生成自定义的随机字母数字字符串...希望获得帮助。

<?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

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

$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 "<br>\n";
echo $string_2;

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

qsBDs4JOoVRfFxyLAOGECYIsWvpcpMzAO9pypwxsqPKeAmYLOi

Ti3kE1WfGgTNxQVXtbNNbhhvvapnaUfGMVJecHkUjHbuCb85pF

希望这就是您想要的...

答案 22 :(得分:0)

简单的“一行”字符串哈希生成器,如 77zd43-7bc495-64cg9a-535548 (1byte = 2chars)

  $hash = implode('-', [
       bin2hex(random_bytes(3)),
       bin2hex(random_bytes(3)),
       bin2hex(random_bytes(3)),
       bin2hex(random_bytes(3)),
    ]);

答案 23 :(得分:0)

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

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

答案 24 :(得分:0)

<?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个字符的随机字符串。

答案 25 :(得分:0)

我认为这是最好的使用方法。

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

答案 26 :(得分:0)

以下是我在其中一个项目中使用的内容,它运行良好,并生成 UNIQUE RANDOM TOKEN

$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,以便为用户可能想知道如何生成此令牌的人造成混淆;)

我希望它有所帮助:)

答案 27 :(得分:0)

斯科特,是的,你是非常好的写作和良好的解决方案!感谢。

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

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;
}

如果我能做任何改进,请查看并告诉我。感谢

答案 28 :(得分:-1)

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

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;
}

有关Random code generator in PHP

的详细信息

答案 29 :(得分:-1)

我相信所有现有想法的问题在于它们可能唯一,但绝对独特(正如Dariusz Walczak对此回复所指出的那样) loletech)。我有一个实际上独一无二的解决方案。它要求您的脚本具有某种内存。对我来说这是一个SQL数据库。你也可以简单地写一个文件。有两种实现方式:

第一种方法:有两个字段而不是一个提供唯一性的字段。第一个字段是一个非随机但唯一的ID号(第一个ID是1,第二个是2 ......)。如果您使用的是SQL,只需使用AUTO_INCREMENT属性定义ID字段即可。第二个字段不是唯一的,而是随机的。这可以通过人们已经提到的任何其他技术生成。斯科特的想法很好,但是md5很方便,而且对于大多数用途来说可能还不错:

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

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

答案 30 :(得分:-2)

通过删除不必要的循环来简化上面的Scotts代码,这种循环正在慢慢减慢并且不会比仅仅调用openssl_random_pseudo_bytes更安全

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)
}