动态验证码,用于双因子登录

mapleSnow 8fc681110d [fix] typo 1 년 전
.gitignore 2e70e1c97e Initial commit 1 년 전
Dockerfile 7ce38a8fed first commit 1 년 전
LICENSE 2e70e1c97e Initial commit 1 년 전
README.md 8fc681110d [fix] typo 1 년 전
composer.json 7ce38a8fed first commit 1 년 전
index.php 7ce38a8fed first commit 1 년 전

README.md

动态验证码部署

快速使用

docker load -i dynamic-qrcode.tar.gz  && docker run -d --name dq -p 8027:80 dynamic-qrcode:v1.0  

编译

docker build -t dynamic-qrcode:v1.0 .

安装

docker load -i dynamic-qrcode.tar.gz 

运行

docker run -d --name dq -p PORT:80 dynamic-qrcode:v1.0  // PORT 按需修改

服务端PHP验证码校验

工具类

<?php

namespace App\Utils;

class TOTPAuthenticator
{
    // Base32 解码函数
    public static function base32_decode($str)
    {
        $base32chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
        $base32charsFlipped = array_flip(str_split($base32chars));
        $paddingCharCount = substr_count($str, '=');

        if ($paddingCharCount > 0) {
            $str = substr($str, 0, -($paddingCharCount));
        }

        $str = str_split($str);
        $binaryString = "";

        for ($i = 0; $i < count($str); $i = $i + 8) {
            $x = "";
            for ($j = 0; $j < 8; $j++) {
                $x .= str_pad(base_convert(@$base32charsFlipped[@$str[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
            }
            $eightBits = str_split($x, 8);
            for ($z = 0; $z < count($eightBits); $z++) {
                $binaryString .= ($eightBits[$z] == '') ? '' : chr(base_convert($eightBits[$z], 2, 10));
            }
        }
        return $binaryString;
    }

    // TOTP 令牌生成函数
    public static function generateTOTPToken($secretKey)
    {
        if (empty($secretKey)) return false;

        $timestamp = floor(time() / 30);
        $validityPeriod = 30 - (time() % 30);
        $secretKey = self::base32_decode($secretKey);
        $timestamp = pack('N*', 0) . pack('N*', $timestamp);
        $hash = hash_hmac('sha1', $timestamp, $secretKey, true);
        $offset = ord(substr($hash, -1)) & 0x0F;
        $token = (
            (ord($hash[$offset + 0]) & 0x7F) << 24 |
            (ord($hash[$offset + 1]) & 0xFF) << 16 |
            (ord($hash[$offset + 2]) & 0xFF) << 8 |
            (ord($hash[$offset + 3]) & 0xFF)
        ) % pow(10, 6);
        return array($token, $validityPeriod);
    }
}

测试用例

<?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;
use App\Utils\TOTPAuthenticator;

class TOTPAuthenticatorTest extends TestCase
{
    // 测试 TOTP 令牌生成函数
    public function testGenerateTOTPToken()
    {
        $secretKey = 'JBSWY3DPEHPK3PXP';
        $tokenInfo = TOTPAuthenticator::generateTOTPToken($secretKey);

        $this->assertIsArray($tokenInfo);
        $this->assertCount(2, $tokenInfo);
        $this->assertIsInt($tokenInfo[0]);
        $this->assertIsInt($tokenInfo[1]);
        $this->assertGreaterThan(0, $tokenInfo[1]);
    }
}