首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 驗證碼進階實現:傾斜、正弦干擾線、字符黏連、旋轉全組合

     編輯:PHP 相關知識

傳統驗證碼易被 OCR 識別。本文實現一款**高強度驗證碼類**:字符隨機旋轉、傾斜、黏連重疊、正弦波干擾線、多字體大小顏色、自適應畫布。核心難點在於**旋轉後字符邊界計算**與**黏連佈局**,附完整類代碼、字體文件說明、Demo 調用示例。

效果特性

  • ✅ 字符**隨機旋轉** (-20°~20°)
  • ✅ 字符**隨機傾斜/字體大小** (17~20px)
  • ✅ 字符**黏連重疊** (間距 0.75 倍字符寬度)
  • ✅ **正弦波干擾線** (振幅/頻率/相位隨機)
  • ✅ 多顏色 (紅/綠/藍) + 白色背景
  • ✅ 自適應畫布寬高 (根據字符實際外包矩形計算)

完整類代碼 (Utils_Caption)

<?php
class Utils_Caption {
    public int $Width = 60;           // 最小寬度
    public int $Height = 30;          // 最小高度
    public int $Length = 4;           // 驗證碼位數
    public string $BgColor = '#FFFFFF';
    public array $TFonts = ['font.ttf']; // 字體文件 (需放同目錄 font/ 下)
    public array $TFontSize = [17, 20];  // 字體大小範圍
    public array $TFontAngle = [-20, 20]; // 旋轉角度範圍
    public string $Chars = '0123456789';  // 字符集 (可加字母)
    public array $Code = [];           // 生成的驗證碼數組 [size, angle, color, char, font, bounds]
    public string $Image = '';
    public array $FontColors = ['#f36161', '#6bc146', '#5368bd'];
    public float $TPadden = 0.75;      // 字符間距係數 (黏連關鍵)
    public int $Txbase = 5;            // 左邊距
    public int $Tybase = 5;            // 上邊距 (內部由 bounds 計算)
    public bool $TLine = true;         // 是否畫干擾線

    // 生成驗證碼數據 (不輸出圖片)
    public function RandRSI(): string {
        $this->TFontAngle = range($this->TFontAngle[0], $this->TFontAngle[1]);
        $this->TFontSize = range($this->TFontSize[0], $this->TFontSize[1]);
        $fontFile = dirname(__FILE__) . '/font/' . $this->TFonts[0];
        $charlen = strlen($this->Chars) - 1;
        $codeStr = '';

        for ($i = 0; $i < $this->Length; $i++) {
            $char = $this->Chars[random_int(0, $charlen)];
            $angle = $this->TFontAngle[random_int(0, count($this->TFontAngle)-1)];
            $size = $this->TFontSize[random_int(0, count($this->TFontSize)-1)];
            $color = $this->FontColors[random_int(0, count($this->FontColors)-1)];
            $bounds = $this->_calculateTextBox($size, $angle, $fontFile, $char);
            $this->Code[] = [$size, $angle, $color, $char, $fontFile, $bounds];
            $codeStr .= $char;
        }
        return $codeStr;
    }

    // 繪製並輸出 PNG
    public function Draw(): void {
        if (empty($this->Code)) $this->RandRSI();
        [$width, $height] = $this->_getImageWH($this->Code);
        $this->Width = $width;
        $this->Height = $height;

        $img = imagecreatetruecolor($width, $height); // 真彩色
        $bg = $this->_getColor2($this->_getColor($this->BgColor));
        imagefilledrectangle($img, 0, 0, $width, $height, $bg);

        $x = $this->Txbase;
        foreach ($this->Code as $v) {
            [$size, $angle, $color, $char, $font, $bounds] = $v;
            $c = $this->_getColor2($this->_getColor($color));
            // imagettftext 基線在字符底部,y = 基線 = 上邊距 + bounds['height']
            imagettftext($img, $size, $angle, $x, $bounds['height'], $c, $font, $char);
            // 下一字符 x: 當前 x + 字符寬度*間距係數 - 左側溢出
            $x += $bounds['width'] * $this->TPadden - $bounds['left'];
        }

        if ($this->TLine) $this->_writeSinLine($img, $x);

        header('Content-Type: image/png');
        imagepng($img);
        imagedestroy($img);
    }

    // 計算字符旋轉後的外包矩形
    private function _calculateTextBox(int $size, int $angle, string $font, string $char): array {
        $box = imagettfbbox($size, $angle, $font, $char);
        $minX = min($box[0], $box[2], $box[4], $box[6]);
        $maxX = max($box[0], $box[2], $box[4], $box[6]);
        $minY = min($box[1], $box[3], $box[5], $box[7]);
        $maxY = max($box[1], $box[3], $box[5], $box[7]);
        return [
            'left'   => ($minX >= -1) ? -abs($minX + 1) : abs($minX + 2),
            'top'    => abs($minY),
            'width'  => $maxX - $minX,
            'height' => $maxY - $minY,
            'box'    => $box,
        ];
    }

    private function _getColor(string $hex): array {
        return [hexdec($hex[1].$hex[2]), hexdec($hex[3].$hex[4]), hexdec($hex[5].$hex[6])];
    }
    private function _getColor2(array $rgb): int {
        return imagecolorallocate($this->Image, $rgb[0], $rgb[1], $rgb[2]);
    }

    // 計算畫布寬高
    private function _getImageWH(array $codes): array {
        $w = $this->Txbase;
        $h = 0;
        foreach ($codes as $v) {
            $w += $v[5]['width'] * $this->TPadden - $v[5]['left'];
            $h = max($h, $v[5]['height']);
        }
        return [max($w, $this->Width), max($h, $this->Height)];
    }

    // 正弦波干擾線
    private function _writeSinLine($img, float $width): void {
        $h = $this->Height;
        $h1 = random_int(-5, 5);   // 垂直偏移
        $h2 = random_int(-1, 1);   // 雙線偏移
        $w2 = random_int(10, 15);  // 頻率分母
        $h3 = random_int(4, 6);    // 振幅分母
        $color = $this->_getColor2($this->_getColor($this->FontColors[array_rand($this->FontColors)]));

        for ($i = -$width/2; $i < $width/2; $i += 0.1) {
            $y = $h/$h3 * sin($i/$w2) + $h/2 + $h1;
            imagesetpixel($img, $i + $width/2, $y, $color);
            if ($h2 != 0) imagesetpixel($img, $i + $width/2, $y + $h2, $color);
        }
    }
}
?>

字體文件要求

  • 目錄結構:`/your/path/font/font.ttf` (類同級 `font/` 目錄)
  • 推薦:粗體無襯線字體 (如 `DejaVuSans-Bold.ttf`、`Microsoft YaHei Bold`),字形清晰、旋轉後辨識度高。
  • 確保 Web 進程對字體文件有讀取權限。

Demo 調用 (驗證碼頁面 captcha.php)

<?php
require_once 'Utils_Caption.php';
session_start();

$captcha = new Utils_Caption();
$captcha->TFontSize = [15, 17];
$captcha->Width = 50;
$captcha->Height = 25;
$code = $captcha->RandRSI();

$_SESSION['CHECKCODE'] = $code; // 存入 Session 驗證
$captcha->Draw(); // 直接輸出圖片
?>

前端表單驗證

<form action="check.php" method="post">
    驗證碼: <input name="code" type="text" maxlength="4">
    <img src="captcha.php" onclick="this.src='captcha.php?'+Math.random()" style="cursor:pointer" title="點擊刷新">
    <button type="submit">提交</button>
</form>

<?php
// check.php
session_start();
if (strtoupper($_POST['code']) === strtoupper($_SESSION['CHECKCODE'])) {
    echo '驗證通過';
} else {
    echo '驗證碼錯誤';
}
?>

安全性增強建議

措施實現方式
字符集擴大`$Chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'` (去除易混淆 0/O/1/I)
增加干擾點/弧線在 `_writeSinLine` 後添加 `imageellipse`、`imagesetpixel` 隨機噪點
背景圖片/漸變用 `imagecreatefromjpeg` 載入背景圖,或 `imagefilledpolygon` 畫漸變多邊形
限制請求頻率同 IP 分鐘請求 > 10 次返回 429,防暴力刷新
Session 驗證碼一次性驗證成功後 `unset($_SESSION['CHECKCODE'])`

現代替代方案提示

高安全場景 (登錄、支付、投票) 建議接入**行為驗證/滑塊驗證/點選驗證** (Geetest、hCaptcha、Turnstile、阿里云驗證碼),前端交互友好、機器識別極難、支持風控模型,避免自研維護成本。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved