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

PHP 生成驗證碼圖片完整教程:從入門到進階實戰

     編輯:PHP 相關知識

前言

驗證碼是 Web 應用防範機器人攻擊、保護表單安全的第一道防線。PHP 搭配 GD 圖形庫,能輕鬆生成包含干擾線、干擾點、扭曲文字的驗證碼圖片。本文將從環境配置、隨機碼生成策略、圖片繪製核心步驟,到完整可用的類封裝與前端整合,為你呈現一份詳盡的實戰指南。

一、環境準備:啟用 GD 庫

修改 php.ini,尋找 ;extension=gd 並移除前方分號,重啟 Web 服務器即可。驗證方式:

<?php phpinfo(); ?>

搜尋 "GD Support" 應顯示 enabled,且支援 FreeType、JPEG、PNG 等格式。

二、隨機驗證碼字串生成四大策略

策略一:預定義字元陣列 + 索引隨機

<?php
$chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
$code = '';
for ($i = 0; $i < 4; $i++) {
    $code .= $chars[mt_rand(0, strlen($chars) - 1)];
}
?>

策略二:ASCII 碼區間控制(數字/大小寫分離)

<?php
function generateCode($length = 4) {
    $code = '';
    for ($i = 0; $i < $length; $i++) {
        $type = mt_rand(0, 2);
        switch ($type) {
            case 0: $ascii = mt_rand(48, 57); break;      // 0-9
            case 1: $ascii = mt_rand(65, 90); break;      // A-Z
            case 2: $ascii = mt_rand(97, 122); break;     // a-z
        }
        $code .= chr($ascii);
    }
    return $code;
}
?>

策略三:字串直接隨機取字

<?php
$pool = 'abcdefghijkmnpqrstuvwxyz23456789'; // 排除易混淆字元
$code = '';
for ($i = 0; $i < 4; $i++) {
    $code .= $pool[mt_rand(0, strlen($pool) - 1)];
}
?>

策略四:十六進位簡碼(極簡場景)

<?php
$code = '';
for ($i = 0; $i < 4; $i++) {
    $code .= dechex(mt_rand(1, 15)); // 1-f
}
?>
安全建議:使用 mt_rand() 而非 rand(),並排除 0/O、1/I/l 等易混淆字元。高安全場景請改用 random_int() (PHP 7+)。

三、核心繪圖流程:從畫布到輸出

步驟 1:建立畫布與配色

<?php
$width = 120; $height = 40;
$img = imagecreatetruecolor($width, $height); // 真彩色畫布

$bgColor = imagecolorallocate($img, 245, 245, 245); // 淺灰背景
$textColor = imagecolorallocate($img, 51, 51, 51);  // 深灰文字
$noiseColor = imagecolorallocate($img, 180, 180, 180); // 干擾色

imagefill($img, 0, 0, $bgColor); // 填充背景
?>

步驟 2:繪製干擾元素(線條/點/弧線)

<?php
// 干擾線
for ($i = 0; $i < 6; $i++) {
    $color = imagecolorallocate($img, mt_rand(150, 255), mt_rand(150, 255), mt_rand(150, 255));
    imageline($img, mt_rand(0, $width), mt_rand(0, $height), mt_rand(0, $width), mt_rand(0, $height), $color);
}

// 干擾點
for ($i = 0; $i < 80; $i++) {
    $color = imagecolorallocate($img, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
    imagesetpixel($img, mt_rand(0, $width), mt_rand(0, $height), $color);
}
?>

步驟 3:繪製驗證碼文字(支援 TTF 字體)

<?php
$fontFile = __DIR__ . '/fonts/arial.ttf'; // 需準備 TTF 字體檔
$code = generateCode(4);
$_SESSION['captcha'] = strtoupper($code); // 存入 Session 驗證

for ($i = 0; $i < 4; $i++) {
    $x = 15 + $i * 24 + mt_rand(-2, 2);
    $y = 28 + mt_rand(-3, 3);
    $angle = mt_rand(-15, 15);
    $color = imagecolorallocate($img, mt_rand(0, 100), mt_rand(0, 100), mt_rand(0, 100));
    imagettftext($img, 20, $angle, $x, $y, $color, $fontFile, $code[$i]);
}
?>

步驟 4:輸出圖片並釋放資源

<?php
header('Content-Type: image/png');
imagepng($img);
imagedestroy($img);
?>

四、完整封裝類:CaptchaGenerator

<?php
class CaptchaGenerator {
    private $width = 130;
    private $height = 45;
    private $length = 4;
    private $fontSize = 22;
    private $fontFile;
    private $charset = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // 排除易混字元
    private $sessionKey = 'captcha_code';

    public function __construct(array $options = []) {
        foreach ($options as $key => $val) {
            if (property_exists($this, $key)) $this->$key = $val;
        }
        $this->fontFile = $this->fontFile ?? __DIR__ . '/fonts/DejaVuSans-Bold.ttf';
        if (!file_exists($this->fontFile)) throw new Exception('字體檔案不存在');
    }

    public function generate(): string {
        $code = '';
        for ($i = 0; $i < $this->length; $i++) {
            $code .= $this->charset[mt_rand(0, strlen($this->charset) - 1)];
        }
        $_SESSION[$this->sessionKey] = strtoupper($code);
        return $this->renderImage($code);
    }

    public function verify(string $input): bool {
        return isset($_SESSION[$this->sessionKey]) &&
               strtoupper($input) === $_SESSION[$this->sessionKey];
    }

    private function renderImage(string $code): void {
        $img = imagecreatetruecolor($this->width, $this->height);
        $bg = imagecolorallocate($img, 255, 255, 255);
        imagefill($img, 0, 0, $bg);

        // 干擾弧線
        for ($i = 0; $i < 5; $i++) {
            $c = imagecolorallocate($img, mt_rand(180, 255), mt_rand(180, 255), mt_rand(180, 255));
            imagearc($img, mt_rand(-10, $this->width), mt_rand(-10, $this->height),
                     mt_rand(30, 300), mt_rand(30, 200), 55, 44, $c);
        }

        // 干擾點
        for ($i = 0; $i < $this->length * 30; $i++) {
            $c = imagecolorallocate($img, mt_rand(0, 255), mt_rand(0, 255), mt_rand(0, 255));
            imagesetpixel($img, mt_rand(0, $this->width), mt_rand(0, $this->height), $c);
        }

        // 繪文字
        for ($i = 0; $i < $this->length; $i++) {
            $x = 12 + $i * 28 + mt_rand(-3, 3);
            $y = $this->height - 10 + mt_rand(-5, 5);
            $angle = mt_rand(-20, 20);
            $c = imagecolorallocate($img, mt_rand(0, 80), mt_rand(0, 80), mt_rand(0, 80));
            imagettftext($img, $this->fontSize, $angle, $x, $y, $c, $this->fontFile, $code[$i]);
        }

        header('Content-Type: image/png');
        header('Cache-Control: no-store, no-cache, must-revalidate');
        header('Pragma: no-cache');
        imagepng($img);
        imagedestroy($img);
    }
}

// 使用範例
session_start();
$captcha = new CaptchaGenerator(['width' => 140, 'length' => 5]);
$captcha->generate(); // 直接輸出圖片
?>

五、前端整合與驗證邏輯

HTML 表單範例

<form action="verify.php" method="post">
    <label>驗證碼:</label>
    <input type="text" name="captcha" required />
    <img id="captchaImg" src="captcha.php" alt="點擊刷新" style="cursor:pointer;vertical-align:middle" />
    <a href="javascript:void(0)" onclick="refreshCaptcha()">看不清?換一張</a>
    <button type="submit">提交</button>
</form>

<script>
function refreshCaptcha() {
    document.getElementById('captchaImg').src = 'captcha.php?' + Date.now();
}
document.getElementById('captchaImg').addEventListener('click', refreshCaptcha);
</script>

後端驗證 (verify.php)

<?php
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = trim($_POST['captcha'] ?? '');
    if (isset($_SESSION['captcha_code']) && strtoupper($input) === $_SESSION['captcha_code']) {
        echo '驗證通過!';
        unset($_SESSION['captcha_code']); // 一次性使用
    } else {
        echo '驗證碼錯誤,請重試。';
    }
}
?>

六、進階優化與安全加固

優化方向實作建議
抗 OCR 識別增加波浪扭曲、隨機字體大小/顏色/旋轉、疊加半透明噪點
效能提升使用 imagecreate() 替代 imagecreatetruecolor()(簡單場景),啟用 OPcache
防重放攻擊驗證成功後立即 unset($_SESSION['captcha']),限制單 Session 最大嘗試次數
無障礙支援提供音頻驗證碼選項,或整合 hCaptcha/reCAPTCHA v3
分散式部署使用 Redis 共享 Session,或採用 JWT 簽名驗證碼(無狀態)

七、常見問題排查

  1. 圖片顯示亂碼/破圖:確保輸出前無任何輸出(含 BOM、空白行),檢查 header() 是否提前發送。
  2. 字體不顯示/報錯:確認 TTF 檔案路徑正確、可讀權限、FreeType 支援已編譯進 GD。
  3. Session 驗證失效:檢查 session_start() 是否在所有輸出前調用、Cookie 域名/路徑設定、跨子域共享設定。
  4. 中文驗證碼:需完整中文字體檔(如 NotoSansTC),字元池改為常用漢字,imagettftext() 直接繪製字串而非單字元。

總結

PHP 生成驗證碼圖片的核心在於:隨機性來源安全、圖形干擾有效性、Session 狀態管理正確。從簡單的腳本到生產級封裝類,關鍵在於細節把控與持續迭代。建議新專案優先考慮成熟的第三方庫(如 gregwar/captcha)或現代無感驗證方案,但在理解原理與客製化需求時,自建方案仍具不可替代的靈活性。

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