ThinkPHP 6 的 Captcha 组件不支持动态背景图,因其底层 GD 库仅接受 RGB 纯色背景配置,无法直接设置 JPG/PNG 背景;需重写 buildImage 或手动 GD 绘制实现。

ThinkPHP 6 的 captcha 组件不支持动态背景图
直接说结论:ThinkPHP 6 内置的 Captcha 验证码生成器(think\captcha\Captcha)底层基于 GD 库,仅支持纯色背景(background 配置项只接受 RGB 数组),**无法直接设置 JPG/PNG 背景图**。所谓“动态背景”,实际得自己接管图像绘制流程——不是改配置,而是重写 buildImage 方法或换用更底层的 GD 操作。
想加干扰线和噪点?别碰 useNoise 和 useCurve 默认值
这两个开关看似简单,但实际效果受字体、字符长度、图像尺寸影响极大:
-
useNoise => true会在字符周围画随机像素点,但若fontSize过小(如 14px)或length较长(如 5 位),噪点极易糊掉字符,反而降低识别率 -
useCurve => true会叠加贝塞尔曲线,但 ThinkPHP 6 的实现是固定路径+固定粗细,在高 DPI 屏幕或缩放后容易断裂或过粗,导致验证码不可读 - 真实可用的组合通常是:
useNoise => false+useCurve => true+ 手动调大imageH(建议 ≥ 50)和fontSize(建议 ≥ 20)
自定义背景+干扰的最小可行方案(GD 手动绘制)
绕过内置 Captcha,直接用 GD 创建资源并写入响应:
// 在控制器中
public function captcha()
{
$im = imagecreatetruecolor(120, 50);
// 1. 加载背景图(需提前准备一张 120x50 的 PNG)
$bg = imagecreatefrompng(ROOT_PATH . 'public/static/captcha_bg.png');
imagecopy($im, $bg, 0, 0, 0, 0, 120, 50);
imagedestroy($bg);
// 2. 手动画干扰线(比 useCurve 更可控)
for ($i = 0; $i < 3; $i++) {
$color = imagecolorallocate($im, rand(180, 220), rand(180, 220), rand(180, 220));
imageline($im, rand(0, 120), rand(0, 50), rand(0, 120), rand(0, 50), $color);
}
// 3. 生成并写入随机字符(注意:需自己做 session 存储)
$code = strtoupper(substr(md5(time() . rand(1000, 9999)), 0, 4));
$_SESSION['captcha'] = $code;
$font = ROOT_PATH . 'public/static/font/arial.ttf';
$text_color = imagecolorallocate($im, 50, 50, 50);
imagettftext($im, 22, rand(-15, 15), 20, 35, $text_color, $font, $code);
// 4. 输出
header('Content-Type: image/png');
imagepng($im);
imagedestroy($im);
}
关键点:背景图必须严格匹配画布尺寸;干扰线颜色要灰度偏高(避免压过文字);字符旋转角度控制在 ±15° 内,否则 OCR 容易失效。
立即学习“PHP免费学习笔记(深入)”;
Session 存储和校验必须手动处理
走自定义绘制路线后,captcha_check() 函数完全失效,因为它的默认逻辑只认内置生成器存的 session key think_captcha:
- 你存的是
$_SESSION['captcha'],就得用strtolower($_SESSION['captcha']) === strtolower($input)校验 - 务必在生成后立即
session_start(),且确保 session 配置未启用use_trans_sid(会污染 URL) - 验证码用一次即失效:校验成功后立刻
unset($_SESSION['captcha']),否则重放攻击可绕过
真正麻烦的从来不是画图,而是让图形可识别、存储可对齐、校验可防重放——背景图再酷,输错三次就锁 IP,那图再动态也没意义。


















