PHP图像验证码生成示例代码

作者:技术拾光

时间:2026-06-07

浏览:332

PHP图像验证码依赖GD图形库,必须确保GD扩展已开启。在Windows系统中,需修改php.ini配置文件,去掉extension=gd前面的分号注释后重启服务;在Linux系统中,可通过编译时添加--with-gd参数或安装php-gd软件包后重启服务。生成验证码的第一步是开启Session会话并进行相关配置。

文章目录

先聊个基础:PHP图像验证码这东西,核心动力全靠GD库撑着。它是PHP官方自带的图像处理扩展,画布创建、颜色分配、文本绘制、图形渲染、图片输出,一套拳法全都能打,属于业界标准方案。

PHP图像验证码生成示例代码

在动手之前,得先保证GD扩展是开着的。

开启GD扩展

  • Windows环境:打开php.ini,把extension=gd前面的分号去掉,然后重启Apache或Nginx之类的Web服务就行。
  • Linux环境:编译PHP的时候加上--with-gd参数,或者直接通过包管理器安装php-gd扩展,比如yum install php-gd或者apt install php-gd,装完重启服务就生效了。

生成验证码

整个过程拆成几步来看,其实并不复杂。

第一步:开启Session与配置

 150,         // 图片宽度
    'height'      => 50,          // 图片高度
    // 字符相关
    'length'      => 4,           // 验证码长度,通常4到6位
    'charset'     => '23456789ABCDEFGHJKLMNPQRSTUVWXYZ', // 字符集:把容易看混的0/O、1/l都去掉了,用户体验会好很多
    'font_size'   => 24,          // 字号
    'font_file'   => './fonts/arial.ttf', // TTF字体文件路径,必须写对,否则文字画不出来
    // 干扰项配置(安全性和可读性之间得平衡好)
    'line_num'    => 6,           // 干扰线条数
    'pixel_num'   => 100,         // 噪点数量
    // 安全配置
    'expire'      => 300,         // 验证码有效期,单位秒,默认5分钟
    'session_key' => 'captcha_code', // Session里存储用的key
];

第二步:创建画布并分配基础颜色

// 创建真彩色画布
$img = imagecreatetruecolor($captchaConfig['width'], $captchaConfig['height']);

// 分配背景色:用浅色系,方便文字识别(RGB值在240到255之间)
$bgColor = imagecolorallocate($img, 245, 245, 245);

// 分配文字颜色:深色系,随机生成,不让OCR容易锁定固定色值
$textColor = imagecolorallocate($img, rand(10, 50), rand(10, 50), rand(10, 50));

// 填充画布背景
imagefill($img, 0, 0, $bgColor);
API 函数核心作用
imagecreatetruecolor(int $width, int $height)创建真彩色图像画布,验证码就画在这上面
imagecolorallocate(resource $image, int $red, int $green, int $blue)给画布分配RGB颜色,背景、文字、干扰项都用得到
imagefill(resource $image, int $x, int $y, int $color)用指定颜色填充画布背景,从坐标(x,y)开始

第三步:生成随机验证码字符串

$code = '';
$charsetLen = strlen($captchaConfig['charset']);
for ($i = 0; $i < $captchaConfig['length']; $i++) {
    // 从字符集里随机抽一个,拼起来就是验证码
    $code .= $captchaConfig['charset'][rand(0, $charsetLen - 1)];
}

第四步:绘制干扰元素——这个顺序有讲究,先把干扰项画上,再写文字,这样文字不会被挡在后面。

// 画随机干扰线
for ($i = 0; $i < $captchaConfig['line_num']; $i++) {
    // 线条颜色用浅色系,别抢了文字的风头
    $lineColor = imagecolorallocate($img, rand(100, 200), rand(100, 200), rand(100, 200));
    // 起点和终点随机生成,保证线条贯穿画布
    imageline(
        $img,
        rand(0, $captchaConfig['width']),
        rand(0, $captchaConfig['height']),
        rand(0, $captchaConfig['width']),
        rand(0, $captchaConfig['height']),
        $lineColor
    );
}

// 画随机噪点
for ($i = 0; $i < $captchaConfig['pixel_num']; $i++) {
    $pixelColor = imagecolorallocate($img, rand(50, 150), rand(50, 150), rand(50, 150));
    imagesetpixel($img, rand(0, $captchaConfig['width']), rand(0, $captchaConfig['height']), $pixelColor);
}
  • imageline(resource $image, int $x1, int $y1, int $x2, int $y2, int $color) 画直线,用来加干扰线,让机器自动识别更费劲。
  • imagesetpixel(resource $image, int $x, int $y, int $color) 画单个像素点,用来制造噪点,加大破解难度。

第五步:把验证码字符画到画布上

// 算一下每个字符该占多宽,让它们在画布上均匀分布
$singleWidth = $captchaConfig['width'] / $captchaConfig['length'];
for ($i = 0; $i < $captchaConfig['length']; $i++) {
    // 每个字符随机旋转-10到10度,防止机器轻松识别
    $angle = rand(-10, 10);
    // 计算x坐标,左右留点边距,均匀排开
    $x = $singleWidth * $i + rand(5, 10);
    // 计算y坐标,垂直居中(基线对齐,避免文字下沉)
    $y = $captchaConfig['height'] / 2 + $captchaConfig['font_size'] / 3;
    // 把单个字符写上去
    imagettftext(
        $img,
        $captchaConfig['font_size'],
        $angle,
        $x,
        $y,
        $textColor,
        $captchaConfig['font_file'],
        $code[$i]
    );
}

imagettftext(resource $image, float $size, float $angle, int $x, int $y, int $color, string $font_file, string $text) 这个函数是核心,负责写TrueType字体的文本,支持自定义字体、字号、旋转角度。

第六步:把验证码存到Session里,完成服务端持久化

// 存一下加密后的验证码和过期时间(生产环境建议加密存储,防止Session泄露)
$_SESSION[$captchaConfig['session_key']] = [
    'code' => strtolower($code), // 转小写,实现不区分大小写校验,用户体验更好
    'expire_time' => time() + $captchaConfig['expire'] // 过期时间戳
];

第七步:把图片输出到浏览器

// 告诉浏览器返回的是PNG图片,并且禁止缓存
header('Content-Type: image/png');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Pragma: no-cache');

// 输出图片
imagepng($img);

// 销毁画布资源,释放内存
imagedestroy($img);
  • imagepng(resource $image) 以PNG格式输出图像,可以直接推送到浏览器,也能存成文件。
  • imagedestroy(resource $image) 销毁画布资源,释放服务器内存,代码收尾时必不可少。
  • header(string $header) 设置HTTP响应头,告诉浏览器返回的是图片,防止页面显示乱码。

校验验证码

 'captcha_code',
];

// 3. 封装一个校验函数,方便全局复用
function checkCaptcha(string $userInput): array
{
    global $captchaConfig;

    // 3.1 先看用户有没有输入验证码
    if (empty($userInput)) {
        return ['status' => false, 'msg' => '请输入验证码'];
    }

    // 3.2 检查服务端的Session里有没有验证码数据
    if (!isset($_SESSION[$captchaConfig['session_key']])) {
        return ['status' => false, 'msg' => '验证码已过期,请刷新重试'];
    }

    // 3.3 取出Session里的验证码信息
    $captchaData = $_SESSION[$captchaConfig['session_key']];

    // 3.4 检查验证码是否过期
    if (time() > $captchaData['expire_time']) {
        // 过期了立即销毁,不让它被复用
        unset($_SESSION[$captchaConfig['session_key']]);
        return ['status' => false, 'msg' => '验证码已过期,请刷新重试'];
    }

    // 3.5 校验验证码是否正确(统一转小写,不区分大小写)
    if (strtolower(trim($userInput)) !== $captchaData['code']) {
        // 校验失败也立即销毁,防止被暴力破解
        unset($_SESSION[$captchaConfig['session_key']]);
        return ['status' => false, 'msg' => '验证码错误,请刷新重试'];
    }

    // 3.6 校验成功,立即销毁当前验证码(单次有效,防止重放攻击)
    unset($_SESSION[$captchaConfig['session_key']]);
    return ['status' => true, 'msg' => '验证码校验通过'];
}

// 4. 调用示例(当用户通过POST提交表单时)
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 获取用户输入的验证码
    $userCaptcha = $_POST['captcha'] ?? '';

    // 执行校验
    $checkResult = checkCaptcha($userCaptcha);
    if (!$checkResult['status']) {
        // 校验失败,返回错误信息
        echo json_encode(['code' => 400, 'msg' => $checkResult['msg']]);
        exit;
    }

    // 校验通过,继续执行后续业务(比如登录、注册、发信息等)
    echo json_encode(['code' => 200, 'msg' => '操作成功']);
    exit;
}

生产环境注意事项与安全规范

到了生产环境,不少细节就得格外上心。

必须使用 HTTPS 传输:验证码相关的接口,包括图片展示和表单提交,都得走HTTPS,防止中间人截获验证码图片或者提交内容,规避重放攻击。

验证码存储安全:

  • 生产环境下,绝对不要把验证码的原始字符串返回给前端,它只应该藏在服务端的Session或Redis里;
  • 如果并发量高,建议用Redis替代原生Session来存验证码,这样性能更好,在分布式系统里也更顺手;
  • 另外,可以考虑对存储的验证码做不可逆加密,万一Session或Redis泄露了,验证码也不容易被破解。

用户体验与安全平衡:

  • 字符集一定要剔除那些容易混淆的,比如0和O、1和l、2和Z,减少用户输错的概率;
  • 干扰线和噪点的数量要合理,别为了安全把人难倒了;
  • 校验时最好不区分大小写,降低输入门槛。

防破解规范:

  • 验证码必须单次有效,不管校验成功还是失败,用完就销毁,绝不复用;
  • 同一个IP如果短时间内反复请求验证码,要做频率限制,防止被批量抓取;
  • 校验失败超过3次,必须强制刷新验证码,防止暴力枚举。

字体与资源规范:

  • 一定要用开源免费的字体,避免商用字体的版权纠纷;
  • 建议准备多种字体,每次随机切换,甚至每个字符用不同字体,能大幅提升OCR的识别难度;
  • 字体文件的路径最好用绝对路径,避免因为相对路径问题导致绘制失败。

输出规范:

  • 必须设置禁止缓存的响应头,防止浏览器或CDN缓存了验证码图片,导致校验失败;
  • 输出图片前,要清空输出缓冲区,避免框架输出的多余内容混进图片里,导致乱码;
  • 图片输出后,一定要调用imagedestroy()销毁画布资源,防止服务器内存泄漏。

严禁存储敏感信息:验证码只用来做“人机校验”,别在里面嵌入用户ID、密码这类敏感信息。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章