Time-based One-time Password algorithm, 是一种共享密钥和当前时间计算一次性密码的算法。
利用时间戳和邮箱(或手机号)生成一段时间内唯一的验证码。验证用户输入的验证码和一段时间内生成的验证码集合对比,如果在此集合,则证明验证码有效。摆脱使用redis等方式存储和对比验证码。
class Totp {
private $salt = 'salt'; // 盐
private $refreshInterval = 30; //每隔30秒刷新验证码
private $checkCount = 10; //验证码集合数量 $refreshInterval x $checkCount 为验证码有效期
private $codeLength = 6; //生成验证码长度
public function generate(string $input) {
return $this->trimTotp($this->genTotp($this->hashInput($input), time()));
}
public function check(string $input, string $code) {
$hashedInput = $this->hashInput($input);
$currentTime = time();
for (
$windowIndex = 0;
$windowIndex <= $this->checkCount;
$windowIndex++
) {
if (
$code === $this->trimTotp(
$this->genTotp(
$hashedInput,
$currentTime - ($windowIndex * $this->refreshInterval)
)
)
) {
return TRUE;
}
}
return FALSE;
}
private function hashInput (string $input) {
$input = $input ? $input : $this->salt;
return hash('sha256', md5($input) . md5($this->salt), FALSE);
}
private function genTotp (string $hashedInput, int $timestamp) {
$sequence = floor($timestamp / 30);
$code = hash_hmac("sha256", $hashedInput . md5($sequence), md5($sequence), TRUE);
$finalValue = 0;
$index = 0;
do {
$finalValue += ord($code[$index]);
$finalValue = $finalValue << 2;
$index++;
} while (isset($code[$index]));
return $finalValue;
}
private function trimTotp (int $sourceTotp) {
$trimedTotp = $sourceTotp % pow(10, $this->codeLength);
$format = "%'.0". $this->codeLength ."u";
return sprintf($format, abs($trimedTotp));
}
}
$email = 'xxxxxx@qq.com';
$totp = new Totp();
$code = $totp->generate($email);
echo $code;
if ($totp->check($email, $code)) {
echo '验证通过';
} else {
echo '验证失败';
}