我试图在PHP中创建一个随机字符串,我得到绝对没有输出:
<?php
function RandomString()
{
$characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
$randstring = '';
for ($i = 0; $i < 10; $i++) {
$randstring = $characters[rand(0, strlen($characters))];
}
return $randstring;
}
RandomString();
echo $randstring;
我做错了什么?
我喜欢使用openssl_random_pseudo_bytes的最后一个注释,但这对我来说不是一个解决方案,因为我仍然必须删除我不想要的字符,而且我无法获得一个设置长度的字符串。这是我的解决方案……
function rndStr($len = 20) {
$rnd='';
for($i=0;$i<$len;$i++) {
do {
$byte = openssl_random_pseudo_bytes(1);
$asc = chr(base_convert(substr(bin2hex($byte),0,2),16,10));
} while(!ctype_alnum($asc));
$rnd .= $asc;
}
return $rnd;
}
这个问题有很多答案,但没有一个是利用加密安全伪随机数生成器(CSPRNG)的。
简单、安全、正确的答案是使用RandomLib,不要白费力气。
对于那些坚持发明自己的解决方案的人,PHP 7.0.0将为此目的提供random_int();如果你还在使用PHP 5。x,我们为random_int()写了一个PHP 5的polyfill,这样你甚至可以在升级到PHP 7之前使用新的API。
在PHP中安全地生成随机整数并不是一项简单的任务。在生产环境中部署自己开发的算法之前,您应该始终与常驻StackExchange密码学专家进行检查。
有了安全的整数生成器,使用CSPRNG生成随机字符串就像在公园里散步一样简单。
创建安全的随机字符串
/**
* Generate a random string, using a cryptographically secure
* pseudorandom number generator (random_int)
*
* This function uses type hints now (PHP 7+ only), but it was originally
* written for PHP 5 as well.
*
* For PHP 7, random_int is a PHP core function
* For PHP 5.x, depends on https://github.com/paragonie/random_compat
*
* @param int $length How many characters do we want?
* @param string $keyspace A string of all possible characters
* to select from
* @return string
*/
function random_str(
int $length = 64,
string $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
): string {
if ($length < 1) {
throw new \RangeException("Length must be a positive integer");
}
$pieces = [];
$max = mb_strlen($keyspace, '8bit') - 1;
for ($i = 0; $i < $length; ++$i) {
$pieces []= $keyspace[random_int(0, $max)];
}
return implode('', $pieces);
}
用法:
$a = random_str(32);
$b = random_str(8, 'abcdefghijklmnopqrstuvwxyz');
$c = random_str();
演示:https://3v4l.org/IMJGF(忽略PHP 5失败;它需要random_compat)
这段代码将帮助:
此函数将返回长度在$maxLength和$minLength之间的随机字符串。
注意:函数random_bytes从PHP 7开始工作。
如果你需要特定的长度,那么$maxLength和$minLength必须相同。
function getRandomString($maxLength = 20, $minLength = 10)
{
$minLength = $maxLength < $minLength ? $maxLength : $minLength;
$halfMin = ceil($minLength / 2);
$halfMax = ceil($maxLength / 2);
$bytes = random_bytes(rand($halfMin, $halfMax));
$randomString = bin2hex($bytes);
$randomString = strlen($randomString) > $maxLength ? substr($randomString, 0, -1) : $randomString;
return $randomString;
}
一个完整的解决方案(课程加测试),部分基于上面的一些建议…
class TokenFactory
{
private const LENGTH = 12;
private const ALLOWED = '123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ~!@#$%^&*{}';
private const MIN_NUMBER_OF_DIGITS = 1;
private const MIN_NUMBER_OF_CAPS = 1;
private const MIN_NUMBER_OF_SPECIALS = 1;
private const MIN_NUMBER_OF_LETTERS = 1;
/**
* @return string
* @throws \Exception
*/
public function make(): string
{
$pass = $this->generateToken();
if ($this->isTokenValid($pass)) {
return $pass;
} else {
return $this->make();
}
}
/**
* @return string
* @throws \Exception
*/
private function generateToken(): string
{
$allowedCharacters = self::ALLOWED;
$token = '';
$max = mb_strlen($allowedCharacters, '8bit') - 1;
for ($i = 0; $i < self::LENGTH; ++$i) {
$token .= $allowedCharacters[random_int(0, $max)];
}
return $token;
}
/**
* @param $token
* @return bool
*/
private function isTokenValid($token): bool
{
$numberOfDigits = preg_match_all("/[0-9]/", $token);
$numberOfCaps = preg_match_all("/[A-Z]/", $token);
$numberOfSpecials = preg_match_all("/[~!@#\$%^&*{}]/", $token);
$numberOfLetters = preg_match_all("/[a-z]/", $token);
return
$numberOfDigits > self::MIN_NUMBER_OF_DIGITS
&& $numberOfCaps > self::MIN_NUMBER_OF_CAPS
&& $numberOfSpecials > self::MIN_NUMBER_OF_SPECIALS
&& $numberOfLetters > self::MIN_NUMBER_OF_LETTERS
;
}
}
class TokenFactoryTest
{
public function test_correct_syntax()
{
/**
* Arrange
*/
$length = 12;
$numberOfChecks = 1000;
/**
* Act & Assert
*/
$class = new TokenFactory();
$i = 0;
while ($i < $numberOfChecks) {
$generatedToken = $class->make();
$numberOfDigits = preg_match_all( "/[0-9]/", $generatedToken );
$numberOfCaps = preg_match_all( "/[A-Z]/", $generatedToken );
$numberOfSpecials = preg_match_all("/[~!@#\$%^&*{}]/", $generatedToken);
$numberOfLetters = preg_match_all("/[a-z]/", $generatedToken);
Assert::assertEquals($length, strlen($generatedToken));
Assert::assertTrue($numberOfDigits >= 1, 'Digit error: ' . $generatedToken);
Assert::assertTrue($numberOfCaps >= 1, 'Caps error: ' . $generatedToken);
Assert::assertTrue($numberOfSpecials >= 1, 'Specials error: ' . $generatedToken);
Assert::assertTrue($numberOfLetters >= 1, 'Letters error: ' . $generatedToken);
$i++;
}
}
}
顺便说一句,请确保在适合您需要的地方捕获该异常!