ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

PHP 验证码可设置字体大小、背景图片与扭曲角度:TaoToken 统一 Key 配置与验证骨架

PHP 验证码可设置字体大小、背景图片与扭曲角度:TaoToken 统一 Key 配置与验证骨架 1. PHP 验证码生成里那些绕不开的配置项PHP 验证码生成是后台登录、注册、评论防刷的常规需求核心就三件事画布上写字、换背景、把字扭一扭。听起来简单但真动手写的时候字体大小、背景图片、扭曲角度这三个参数经常互相打架——字太小看不清背景太花盖住字扭曲角度太大直接扭出画布。我试过把角度调到 ±30 度结果验证码变成了一堆斜线用户根本认不出来。这篇内容面向正在用 PHP 做验证码的开发者尤其是已经写过imagettftext但发现参数不好调、想把这套逻辑接进统一 API 通道的人。我会先给出一个可运行的验证码骨架把字体大小、背景图片、扭曲角度做成可配置项然后说明怎么通过 TaoToken 的统一 Key 和 API 通道来管理这类图像生成服务的调用配置。TaoToken 在这里的角色是统一管理 API Key 和请求通道让你不用在多个服务之间来回切换密钥官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。验证码本身是本地 GD 库生成的不需要外部服务但如果你想把验证码参数配置、字体文件路径、背景图目录这些信息统一管理或者后续要接入模型做验证码识别测试用 TaoToken 的 Key 体系会更顺。下面从环境准备开始一步步把配置骨架搭起来。2. TaoToken 统一 Key 与配置通道准备在写验证码代码之前先把配置管理这块理清楚。TaoToken 提供统一的 API Key 和请求通道你可以把它理解成一个密钥管家所有需要调用的服务都走同一个入口Key 只在 TaoToken 控制台生成一次。对于验证码场景你可能会用到两类能力一是本地 GD 生成验证码二是后续用模型做验证码可读性测试或 OCR 对比。后者就需要通过 API 通道调用。先去控制台创建 Key地址是 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 。创建完成后你会拿到一个以sk-开头的字符串。这个 Key 不要硬编码在 PHP 文件里建议放到项目根目录的.env或者独立的配置文件。如果你用的是 Composer 项目可以配合vlucas/phpdotenv读取。TaoToken 的 API 基础地址是 https://taotoken.net/api 所有请求都走这个入口。验证码生成本身不消耗 API 额度但如果你要调用模型对话来测试验证码文字是否可识别就需要在请求头里带上Authorization: Bearer sk-xxxx。模型对话入口在 https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 你可以用它来验证生成的验证码文字是否清晰。配置骨架分两份一份是 PHP 项目用的settings.json一份是如果你用其他语言或工具链时的config.toml。两份内容语义一致只是格式不同。下面先给settings.json。{ taotoken: { api_base: https://taotoken.net/api, api_key_env: TAOTOKEN_API_KEY, model_chat_path: /v1/chat/completions }, captcha: { width: 140, height: 50, font_path: ./fonts/Astarisborn.TTF, font_size_min: 18, font_size_max: 26, background_dir: ./backgrounds, background_count: 8, distort_angle_max: 12, char_count: 4, char_spacing: 28 } }对应的config.toml如下字段含义完全一致方便你在非 PHP 环境或 CI 脚本里复用同一套参数。[taotoken] api_base https://taotoken.net/api api_key_env TAOTOKEN_API_KEY model_chat_path /v1/chat/completions [captcha] width 140 height 50 font_path ./fonts/Astarisborn.TTF font_size_min 18 font_size_max 26 background_dir ./backgrounds background_count 8 distort_angle_max 12 char_count 4 char_spacing 28这里有几个参数需要解释。font_size_min和font_size_max控制字体大小的随机范围比固定一个值更安全因为攻击者难以通过固定字高做模板匹配。distort_angle_max是扭曲角度的绝对值上限实际生成时会在-max到max之间随机取整数。background_count表示背景图目录里有几张图代码会随机选一张。char_spacing是字符之间的水平间距原来 excerpt 里写死 15在 140 宽的画布上四个字会挤在一起改成 28 更合适。Key 的读取方式建议用环境变量不要写进 JSON。你可以在.env里写TAOTOKEN_API_KEYsk-你的真实key然后在 PHP 里用getenv(TAOTOKEN_API_KEY)取。如果你还没有 Key去 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite 创建。3. 可复制的验证码生成骨架与参数配置下面给出完整的 PHP 验证码生成类把字体大小、背景图片、扭曲角度三项都做成从配置读取。代码基于 GD 扩展确保你的 PHP 已经启用gd和mbstring。如果你用的是 Docker基础镜像里通常需要docker-php-ext-install gd。?php class CaptchaGenerator { private array $cfg; private string $fontPath; private string $bgDir; public function __construct(array $config) { $this-cfg $config[captcha]; $this-fontPath $this-cfg[font_path]; $this-bgDir $this-cfg[background_dir]; } private function randomString(int $len): string { $chars 23456789abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ; $str ; $max strlen($chars) - 1; for ($i 0; $i $len; $i) { $str . $chars[random_int(0, $max)]; } return $str; } private function randomBackground(): ?GdImage { $files glob($this-bgDir . /*.{jpg,jpeg,png}, GLOB_BRACE); if (empty($files)) { return null; } $pick $files[array_rand($files)]; $ext strtolower(pathinfo($pick, PATHINFO_EXTENSION)); if ($ext png) { return imagecreatefrompng($pick); } return imagecreatefromjpeg($pick); } public function render(): void { $w $this-cfg[width]; $h $this-cfg[height]; $img imagecreatetruecolor($w, $h); $bg $this-randomBackground(); if ($bg ! null) { $bgW imagesx($bg); $bgH imagesy($bg); $srcX random_int(0, max(0, $bgW - $w)); $srcY random_int(0, max(0, $bgH - $h)); imagecopy($img, $bg, 0, 0, $srcX, $srcY, $w, $h); imagedestroy($bg); } else { $white imagecolorallocate($img, 245, 245, 245); imagefill($img, 0, 0, $white); } $text $this-randomString($this-cfg[char_count]); $spacing $this-cfg[char_spacing]; $angleMax $this-cfg[distort_angle_max]; for ($i 0; $i strlen($text); $i) { $size random_int($this-cfg[font_size_min], $this-cfg[font_size_max]); $angle random_int(-$angleMax, $angleMax); $x 10 $i * $spacing; $y random_int((int)($h * 0.6), (int)($h * 0.85)); $color imagecolorallocate( $img, random_int(0, 120), random_int(0, 120), random_int(0, 120) ); imagettftext($img, $size, $angle, $x, $y, $color, $this-fontPath, $text[$i]); } header(Content-Type: image/png); header(Cache-Control: no-store, no-cache, must-revalidate); imagepng($img); imagedestroy($img); } }调用方式很简单把配置数组传进去就行。$config json_decode(file_get_contents(__DIR__ . /settings.json), true); $captcha new CaptchaGenerator($config); $captcha-render();这里有几个和 excerpt 不同的地方值得说明。第一字符集去掉了容易混淆的0、1、l、o减少用户识别错误。第二背景图选取用glob扫描目录不再依赖固定命名规则你往backgrounds目录里丢任意 jpg 或 png 都能用。第三扭曲角度用random_int(-$angleMax, $angleMax)比原来array_rand加正负号的方式更直观也更容易通过配置调整。第四字体大小每次随机范围由配置控制。如果你要把验证码文字存到 session 里做校验在render()之前把$text返回出来即可。建议改成render(): string返回文字然后在调用处写入$_SESSION[captcha]。4. 验证请求与成功结果确认代码写完后先别急着接进登录页用命令行单独跑一次确认图片能正常输出。在项目目录下执行php -r \$config json_decode(file_get_contents(settings.json), true); require CaptchaGenerator.php; \$c new CaptchaGenerator(\$config); \$c-render(); /tmp/captcha_test.png然后检查文件类型和尺寸file /tmp/captcha_test.png # 期望输出PNG image data, 140 x 50, 8-bit/color RGB, non-interlaced如果file命令显示的是 PNG 且尺寸为 140x50说明画布和输出都正常。接着用图片查看器打开确认三件事背景图有随机裁切、字符大小有明显差异、字符有轻微倾斜但不影响阅读。如果背景是纯白说明background_dir路径不对或者目录里没有图片检查配置里的相对路径是否相对于 PHP 执行目录。接下来验证 TaoToken 通道是否可用。虽然验证码生成不依赖 API但你可以用模型对话接口来测试验证码文字的可读性。先确认 Key 能正常读取echo $TAOTOKEN_API_KEY | head -c 8 # 期望输出sk-xxxxx前8位然后发一个最小请求到模型对话入口确认通道连通curl -s -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: gpt-4o-mini, messages: [{role: user, content: 回复 ok}], max_tokens: 10 } | head -c 200如果返回的 JSON 里有choices字段说明 Key 和通道都正常。这一步不是验证码生成的必需步骤但如果你后续要做验证码识别测试这个通道就是基础。模型对话的完整入口在 https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 你可以在那里直接测试。成功结果的标准是本地生成 PNG 正常、尺寸正确、背景和字体参数生效、TaoToken 通道返回 200。四项都通过就可以把验证码接进业务代码了。5. 本篇常见错排查验证码生成过程中最容易踩的坑集中在字体、背景和角度三块。下面按报错现象列出来。字体文件找不到或格式不支持。报错通常是imagettftext(): Could not find/open font。先确认font_path是绝对路径还是相对路径GD 对相对路径的处理依赖当前工作目录建议用__DIR__ . /fonts/xxx.TTF拼绝对路径。另外 TTF 文件要有读取权限Linux 下检查ls -l的权限位。如果字体文件损坏GD 会静默失败换一个已知可用的 TTF 测试。背景图片粘贴后全黑或全白。常见原因是imagecopy的源坐标超出背景图范围。原 excerpt 里$bg_xrand(0,130)但背景图可能只有 100 宽导致越界。上面的代码用max(0, $bgW - $w)限制了随机范围确保裁切区域不越界。另外 PNG 背景图如果带透明通道imagecreatefrompng后直接imagecopy到真彩色画布上透明区域会变成黑色需要先imagefill一个底色再粘贴。扭曲角度过大导致字符出画布。当distort_angle_max超过 20 度且字符靠近画布边缘时旋转后的字形可能超出边界被裁掉。解决办法有两个一是把distort_angle_max控制在 15 以内二是给每个字符的 x 坐标留出足够边距上面代码用10 $i * $spacing起始边距 10 像素配合 140 宽度和 28 间距四个字符总占用约 103*28字宽不会溢出。验证码文字和 session 不一致。如果你在render()里生成文字但没有返回session 里存的是旧值。把render()改成返回$text在输出图片前写入 session。注意header()之前不能有任何输出包括 BOM 和空格。TaoToken 请求返回 401。检查Authorization头是否带了Bearer前缀Key 是否有多余空格。如果 Key 是从文件读取的注意换行符。另外确认请求地址是https://taotoken.net/api/v1/chat/completions不要漏掉/api前缀。GD 扩展未启用。报错Call to undefined function imagecreatetruecolor()。在php.ini里取消extensiongd的注释重启 PHP-FPM 或 Apache。用php -m | grep gd确认。6. 配置生效后的接入与后续动作验证码骨架跑通后接下来就是把它接进实际业务。登录页的图片标签直接指向生成脚本例如img src/captcha.php onclickthis.src/captcha.php?Math.random()加随机参数避免浏览器缓存。校验时从 session 取文字用strtolower统一大小写后比较。如果你要把这套配置带到更多项目里TaoToken 的 Key 和通道可以统一管理。长期做编码和 Agent 集成的场景可以看看 Coding Plan入口在 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 它适合需要持续调用模型能力的开发流程。接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有完整的请求示例和参数说明。如果你用 Claude Code 做开发Anthropic 兼容入口在 https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaude-code-anthropicutm_campaignrewrite 。最后提醒一点验证码的字体大小、背景图片、扭曲角度这三个参数没有万能值需要根据你的用户群体调整。移动端用户多就把字体调大、角度调小安全要求高就增加背景干扰线和噪点。配置骨架的价值在于让你改一个数字就能看到效果而不是每次翻代码找写死的常量。
返回列表