TOTP身份驗證的工作原理基于時間戳和密鑰。用戶需要在設置階段將密鑰與相應的身份驗證器進行綁定。通常,用戶會在需要使用TOTP動態驗證碼的APP或網站上獲得一個密鑰,然后將該密鑰添加到TOTP驗證器工具上。驗證器會根據當前的時間戳和密鑰生成一個一次性密碼(通常是6位數字),用戶需要將這個密碼輸入到需要驗證的系統中以完成身份驗證。
1、針對某個登錄用戶生成秘鑰
<?php
//生成32位的秘鑰
function generateSecret($length = 32) {$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'; // Base32 字符集$secret = '';for ($i = 0; $i < $length; $i++) {$secret .= $characters[random_int(0, strlen($characters) - 1)];}return $secret;
}$secret = generateSecret(); // 用戶的唯一 Secret
echo "Secret Key: $secret";
2、生成某個登錄用戶需要綁定到Authenticator的鏈接(二維碼形式顯示)
<?php
function generateQRCodeUrl($appName, $userEmail, $secret) {$issuer = urlencode($appName);$email = urlencode($userEmail);return "otpauth://totp/{$issuer}:{$email}?secret={$secret}&issuer={$issuer}";
}$appName = "xxx-test";//在app列表中顯示的名稱
$userEmail = "xxx.xxx@gmail.com";
$secret='4IRT6WN2RHWZHJ4ZOO2POMYB6W43FQ7G';
$qrCodeUrl = generateQRCodeUrl($appName, $userEmail, $secret);
echo $qrCodeUrl;//當前鏈接生成為二維碼的鏈接
//echo '<img src="' . $qrCodeUrl . '" alt="QR Code">';
3、登錄的時候驗證用戶輸入的code是否正確
<?php
/*** 生成動態驗證碼(驗證用)*/
function getOtp($secret, $timeSlice = null) {if ($timeSlice === null) {$timeSlice = floor(time() / 30); // 當前時間戳除以30秒}$secretKey = base32Decode($secret); // 解碼 Base32 密鑰$time = pack('N*', 0) . pack('N*', $timeSlice); // 將時間戳轉換為 64 位二進制// 使用 HMAC-SHA1 計算哈希值$hmac = hash_hmac('sha1', $time, $secretKey, true);// 截取動態偏移量$offset = ord(substr($hmac, -1)) & 0x0F;$binary = (ord($hmac[$offset]) & 0x7F) << 24 |(ord($hmac[$offset + 1]) & 0xFF) << 16 |(ord($hmac[$offset + 2]) & 0xFF) << 8 |(ord($hmac[$offset + 3]) & 0xFF);// 生成6位數字驗證碼return str_pad($binary % pow(10, 6), 6, '0', STR_PAD_LEFT);
}function base32Decode($base32) {$characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567';$base32 = strtoupper($base32);$bits = '';foreach (str_split($base32) as $char) {$bits .= str_pad(decbin(strpos($characters, $char)), 5, '0', STR_PAD_LEFT);}$bytes = '';foreach (str_split($bits, 8) as $byte) {$bytes .= chr(bindec($byte));}return $bytes;
}// $otp = getOtp($secret);
// echo "Generated OTP: $otp".PHP_EOL;
//驗證方法
function verifyOtp($secret, $userInput, $window = 1) {$timeSlice = floor(time() / 30);//TOTP 的核心是基于當前時間的30秒時間片。一個時間片的公式是for ($i = -$window; $i <= $window; $i++) {echo "OTP: " . getOtp($secret, $timeSlice + $i) . PHP_EOL;echo "$i:$window".PHP_EOL;if (getOtp($secret, $timeSlice + $i) === $userInput) {return true;}}return false;
}
//index.php 生成的secret
$secret='4IRT6WN2RHWZHJ4ZOO2POMYB6W43FQ7G';
$userInput = '927055'; // 用戶輸入的動態驗證碼
if (verifyOtp($secret, $userInput)) {echo "驗證通過!";
} else {echo "驗證碼錯誤,請重試。";
}