使用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 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”)。