thinkphp 6+ 应使用内置 captcha 类而非手写图像逻辑,因其自动绑定 session、防安全漏洞且稳定;校验需传对 $id 并启用自动清除或手动 unset,配合 ip 限流与防缓存措施。

验证码生成用 think\captcha\Captcha 类就行,别自己手写图像逻辑
ThinkPHP 6+ 内置的 Captcha 类封装了 GD 或 Imagick(需扩展支持),默认用 GD,够用且稳定。手动拼接字符、画背景、加噪点、写文字不仅容易出安全漏洞(比如字符可预测、无 session 绑定),还可能因 PHP 图像扩展缺失直接报错。
生成时关键不是“怎么画图”,而是“怎么绑定用户上下文”——必须依赖 session,否则攻击者可批量刷取同一张验证码反复尝试。默认配置下,Captcha 会自动把验证码字符串存入 $_SESSION['captcha_' . $id],$id 默认是 name 参数值(如 'captcha')。
示例:
use think\captcha\Captcha;
$captcha = new Captcha();
// 可选配置,但不建议改太激进(比如字符集删掉数字,反而降低熵值)
$captcha->codeSet = '2345678abcdefhijkmnpqrstuvwxyz'; // 避免易混淆字符
$captcha->length = 4;
$captcha->useNoise = true;
$captcha->useCurve = false; // 曲线干扰在移动端识别率低,且防 OCR 效果有限
return $captcha->create('login'); // 返回 base64 图片数据或直接输出(取决于调用方式)
verify() 校验必须传对 $id,且校验后立即销毁 session 中的值
很多人只调 Captcha::check($code),却忽略它默认使用 'captcha' 这个 $id。如果你生成时用了 create('login'),校验时就必须显式传 'login',否则读的是 $_SESSION['captcha_captcha'],永远失败。
更关键的是:校验通过后,check() 不会自动清除 session 值。如果不清除,同一验证码可被重复提交多次——这是典型逻辑漏洞。
- 正确做法:显式传
$id,并确认返回true后手动unset($_SESSION['captcha_login']) - 或者用
Captcha::check($code, 'login', true)—— 第三个参数为true表示校验成功后自动删除 session 值(TP 6.0.12+ 支持) - 别在前端用 JS 拼接
code后直接发请求校验,必须走后端完整流程(含 session 验证),否则绕过登录接口也能调用校验逻辑
验证码接口要防暴力刷,得加基础限流,不能只靠图形复杂度
图形再扭曲,也挡不住每秒几十次的自动化请求。TP 自身不带限流中间件,得自己补:
- 用
Cache记录 IP + 接口路径的请求次数,比如cache('captcha_rate_limit_' . $ip, 0, 3600)每小时最多 10 次 - 生成接口返回前检查:若超限,直接返回错误(如
['code'=>429, 'msg'=>'请求过于频繁']),不生成图片 - 别把限流逻辑写在控制器里——应放在中间件或服务层,避免控制器重复写
- 注意:CDN 或代理后获取真实 IP 要用
request()->ip(true),否则限流基于代理 IP 失效
前后端交互时,src 地址必须带时间戳或随机参数防缓存
浏览器缓存验证码图片会导致用户看到旧图,但提交的是新图的值,必然失败。常见错误是写死 <img src="/captcha">,没加防缓存参数。
正确做法:
- 前端:
<img :src="'/captcha?'+Math.random()">或<img src="/captcha?t="> - 后端接口路由别用 GET 参数传
$id(如/captcha?name=login),而要用 PATH_INFO(如/captcha/login),避免 CDN 缓存不同$id的响应 - 如果用了 Nginx,确认没配
expires 1h这类规则作用于/captcha*路径
验证码不是万能盾,它只增加自动化攻击成本。真正该花精力的地方是:session 安全(HttpOnly + Secure)、密码策略、登录失败锁定、以及——别让验证码成为唯一防线。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











