使用gii生成用户表的model生成的几个方法解析:
随机数生成
protected function generateSalt($cost = 13) { $cost = (int) $cost; if ($cost < 4 || $cost > 31) { throw new InvalidParamException('Cost must be between 4 and 31.'); } $rand = $this->generateRandomKey(20); $salt = sprintf("$2y$%02d$", $cost); $salt .= str_replace('+', '.', substr(base64_encode($rand), 0, 22)); return $salt; }
(1)、生成随机数:$rand = $this->generateRandomKey(20);
(2)、加上前缀:$salt = sprintf("$2y$%02d$", $cost); $cost=13时默认前缀为$2y$13$
(3)、将$rand用base64加密,取前22位,并将+替换为.,最后加上前缀返回
原理,使用blowfish标准加密生成60个字符的hash,利用salt的最大长度(个人理解,salt最大长度应该是在21到22个字符之间),将hash作为salt与明文密码加密可得出一样的结果;密码最大为74个字符,超出则与74个字符密文相同;
setPassword()方法:
2、validatePassword($password)的方法
该方法会调用yii2/base/Security的validPassword()方法,
Security中的validPassword()方法会将用户输入的密码与数据库中的hash值一起加密
$test = crypt($password, $hash);
$n = strlen($test);
if ($n !== 60) {
return false;
}
return $this->compareString($test, $hash);
然后返回compareString($test, $hash)方法的结果
compareString方法:
//加上结束标记,防止mb_strlen()函数找不到结束标记 $expected .= "\0"; $actual .= "\0"; //调用mb_strlen($string, '8bit')函数按位获取字符串长度 $expectedLength = StringHelper::byteLength($expected); $actualLength = StringHelper::byteLength($actual); //获得长度差值 $diff = $expectedLength - $actualLength; //循环对比每一位的ascii值是否相等(先将密文hash与用户输入加密后的字符串按位与,然后与diff按位或,返回结果) for ($i = 0; $i < $actualLength; $i++) { $diff |= (ord($actual[$i]) ^ ord($expected[$i % $expectedLength])); } return $diff === 0;
用到的相关函数:
ord():
回字符串第一个字符的 ASCII 值
crypt(str,salt):
str:必需。规定要编码的字符串。
salt:可选。用于增加被编码字符数目的字符串,以使编码更加安全。如果未提供 salt 参数,则每次调用该函数时会随机生成一个。
mb_strlen():
获取字符串的长度,第二个参数为字符编码。如果省略,则使用内部字符编码。
PHP相关位运算符:
$a & $b And(按位与) 将把 $a 和 $b 中都为 1 的位设为 1。
$a | $b Or(按位或) 将把 $a 和 $b 中任何一个为 1 的位设为 1。
$a ^ $b Xor(按位异或) 将把 $a 和 $b 中一个为 1 另一个为 0 的位设为 1。
~ $a Not(按位取反) 将 $a 中为 0 的位设为 1,反之亦然。
$a << $b Shift left(左移) 将 $a 中的位向左移动 $b 次(每一次移动都表示“乘以 2”)。
$a >> $b Shift right(右移) 将 $a 中的位向右移动 $b 次(每一次移动都表示“除以 2”)。