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

PHP 驗證碼類封裝實戰:從基礎 GD 繪圖到可擴展的驗證組件

     編輯:PHP 綜合知識

前言

驗證碼是防止機器人濫用表單的基礎防線。本文展示一個基於 GD 庫的面向對象驗證碼類,支援自定義字符集、尺寸、干擾元素、Session 存儲,並分析擴展方向(如幹擾線、字體、驗證邏輯解耦)。

完整類實現

<?php
/**
 * 驗證碼生成類
 * 依賴:GD 擴展、Session
 */
class SecurityCode
{
    /** @var string 生成的驗證碼字符串 */
    private string $code = '';

    /** @var int 圖片寬度 */
    private int $width = 100;

    /** @var int 圖片高度 */
    private int $height = 40;

    /** @var int 驗證碼長度 */
    private int $length = 4;

    /** @var string 字符集 */
    private string $charset = '0123456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // 排除易混淆字符

    /** @var string Session 鍵名 */
    private string $sessionKey = 'captcha_code';

    /** @var resource|null GD 圖片資源 */
    private $image = null;

    public function __construct(array $options = [])
    {
        foreach ($options as $key => $value) {
            if (property_exists($this, $key)) {
                $this->$key = $value;
            }
        }
        // 確保 Session 已啟動
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
    }

    /**
     * 生成隨機驗證碼字符串
     */
    private function generateCode(): string
    {
        $chars = str_split($this->charset);
        shuffle($chars);
        $this->code = implode('', array_slice($chars, 0, $this->length));
        return $this->code;
    }

    /**
     * 創建圖片資源並繪製背景
     */
    private function createImage(): void
    {
        $this->image = imagecreatetruecolor($this->width, $this->height);
        // 背景色(淺灰)
        $bgColor = imagecolorallocate($this->image, 230, 230, 230);
        imagefill($this->image, 0, 0, $bgColor);
    }

    /**
     * 繪製干擾元素(噪點、干擾線)
     */
    private function drawDistortion(): void
    {
        // 噪點
        for ($i = 0; $i < 50; $i++) {
            $color = imagecolorallocate($this->image, mt_rand(150, 255), mt_rand(150, 255), mt_rand(150, 255));
            imagesetpixel($this->image, mt_rand(0, $this->width), mt_rand(0, $this->height), $color);
        }
        // 干擾線
        for ($i = 0; $i < 3; $i++) {
            $color = imagecolorallocate($this->image, mt_rand(100, 200), mt_rand(100, 200), mt_rand(100, 200));
            imageline($this->image, mt_rand(0, $this->width), mt_rand(0, $this->height), mt_rand(0, $this->width), mt_rand(0, $this->height), $color);
        }
    }

    /**
     * 繪製驗證碼字符
     */
    private function drawCode(): void
    {
        $colors = [
            imagecolorallocate($this->image, 255, 0, 0),
            imagecolorallocate($this->image, 0, 100, 0),
            imagecolorallocate($this->image, 0, 0, 255),
            imagecolorallocate($this->image, 150, 0, 150),
        ];

        $fontSize = 5; // GD 內建字體 1-5
        $charWidth = $this->width / $this->length;

        for ($i = 0; $i < $this->length; $i++) {
            $x = $i * $charWidth + mt_rand(5, 10);
            $y = mt_rand(5, $this->height - 10);
            $color = $colors[array_rand($colors)];
            $angle = mt_rand(-15, 15); // 旋轉角度

            // 使用 TTF 字體可替換為 imagettftext()
            imagestring($this->image, $fontSize, $x, $y, $this->code[$i], $color);
        }
    }

    /**
     * 輸出圖片並銷毀資源
     */
    private function output(): void
    {
        header('Content-Type: image/png');
        header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
        header('Pragma: no-cache');
        imagepng($this->image);
        imagedestroy($this->image);
    }

    /**
     * 公開入口:生成並輸出驗證碼圖片
     */
    public function render(): void
    {
        $this->generateCode();
        $_SESSION[$this->sessionKey] = $this->code; // 存入 Session
        $this->createImage();
        $this->drawDistortion();
        $this->drawCode();
        $this->output();
    }

    /**
     * 靜態驗證方法(解耦使用)
     */
    public static function verify(string $input, string $sessionKey = 'captcha_code'): bool
    {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
        $stored = $_SESSION[$sessionKey] ?? '';
        // 驗證後立即銷毀,防止重放攻擊
        unset($_SESSION[$sessionKey]);
        return strtoupper($input) === strtoupper($stored);
    }
}

// ===== 使用示例 =====
// captcha.php - 專門輸出驗證碼圖片的入口
// require 'SecurityCode.php';
// $captcha = new SecurityCode(['width' => 120, 'height' => 40, 'length' => 5]);
// $captcha->render();
// exit;

// verify.php - 表單提交驗證
// if (SecurityCode::verify($_POST['captcha'] ?? '')) {
//     echo '驗證通過';
// } else {
//     echo '驗證碼錯誤';
// }

核心 GD 函數解析

函數用途關鍵參數
imagecreatetruecolor()創建真彩色圖片(支援 1670 萬色)寬、高
imagecolorallocate()分配顏色 ID圖片資源、R、G、B
imagefill()填充背景圖片、起始座標、顏色 ID
imagesetpixel()繪製單個像素(噪點)圖片、x、y、顏色 ID
imageline()繪製直線(干擾線)圖片、x1、y1、x2、y2、顏色 ID
imagestring()繪製水平字符串(內建字體)圖片、字體號(1-5)、x、y、字符串、顏色 ID
imagettftext()繪製 TTF 字體文本(推薦生產用)圖片、字號、角度、x、y、顏色、字體路徑、文本
imagepng()輸出 PNG 格式圖片資源
imagedestroy()釋放圖片內存圖片資源

進階擴展方向

1. 使用 TTF 字體提升可讀性與安全性

// 替換 drawCode() 內部
$fontFile = __DIR__ . '/fonts/DejaVuSans.ttf';
imagettftext($this->image, 20, $angle, $x, $y, $color, $fontFile, $this->code[$i]);

優點:字體豐富、抗鋸齒、可變粗細、更難被 OCR 識別。

2. 算術/邏輯驗證碼(無需 Session 存儲答案)

class MathCaptcha {
    public function render(): void {
        $a = mt_rand(1, 20);
        $b = mt_rand(1, 20);
        $op = ['+', '-', '*'][mt_rand(0, 2)];
        $question = "$a $op $b";
        $answer = match($op) {
            '+' => $a + $b,
            '-' => $a - $b,
            '*' => $a * $b,
        };
        $_SESSION['captcha_answer'] = $answer;
        // 繪製 $question 到圖片...
    }
}

3. 結合 Redis 替代 Session(分佈式環境)

// 存儲:Redis SETEX captcha:{token} 300 {code}
// 驗證:Redis GET + DEL captcha:{token}
// 前端需攜帶 token 參數

4. 無障礙支援:音頻驗證碼

使用 espeak 或 Google TTS API 生成語音文件,提供音頻播放按鈕。

安全最佳實踐

  • 驗證後立即銷毀 Session 值: 防止重放攻擊(見 verify() 方法)
  • 限制請求頻率: 同一 IP 分鐘內請求驗證碼不超過 N 次
  • HTTPS 強制傳輸: 防止中間人竊聽驗證碼圖片
  • 組合防禦: 驗證碼 + 限流 + 設備指紋 + 行為分析,單一驗證碼不可靠
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved