thinkphp本身不自动用验证码拦截爬虫,需主动设计触发逻辑;应页面加载时即通过js注入_ts字段、校验cookie/referer/ua等行为特征,并结合中间件轻量过滤与风险评分动态触发验证码。

ThinkPHP 本身不自动用验证码拦截爬虫,验证码只是工具,是否触发、何时触发、对谁触发,全靠你主动设计逻辑。单纯加个验证码图片在登录页,对现代爬虫基本无效——它们能自动识别图像、调用 OCR 接口、甚至模拟 JS 行为。
验证码不能硬绑在提交按钮上
很多开发者把验证码校验写在表单 POST 处理里,等用户点“登录”才校验。这等于把门锁装在屋子里,小偷早进来了。真正有效的做法是:页面一加载就开始收集风险信号,动态决定要不要弹出验证码。
- 首次访问时,前端 JS 注入一个隐藏字段(如 _ts),记录页面加载完成到点击提交的毫秒差
- 服务端收到请求后比对:当前时间 - _ts → 极大概率是脚本提交,直接拦截或强制走验证码流程
- 检查 request()->cookie('thinkphp_token') 是否存在且未过期——真实用户有会话,多数爬虫不维持 Cookie
- 验证 Referer 是否来自本站、X-Requested-With 是否为 XMLHttpRequest,缺失即高风险
用中间件做 UA 初筛 + 风险标记
User-Agent 不是用来拦爬虫的,而是快速放行可信客户端(如自家 App)、同时标出明显异常流量(空 UA、含 python-requests 却走 Web 路由)。它必须配合其他维度一起判断。
- 在 app/middleware/UserAgentFilter.php 中统一处理,别写在控制器里
- 白名单配置放在 app/extra/spider.php,内容如
return ['Chrome', 'Mobile Safari', 'WeChat']; - 匹配用 stripos(),不是全等,避免因版本号、平台字段差异误判
- 空 UA、纯数字字符串(如 123456)、含 curl/httpie/python-requests 的请求,记日志并打上 is_suspicious = true 标签,供后续风控逻辑使用
验证码校验要带 ID、带上下文
多个页面共用一个验证码,或不同业务场景(登录 / 注册 / 找回密码)混用同一组 Session 值,会导致校验混乱、绕过容易。
- 模板中调用 {:captcha_img('login')},指定唯一标识
- 控制器中校验时传相同 ID:captcha_check($code, 'login')
- 验证码接口(如
index/captcha)建议加随机参数或时间戳,防缓存复用 - 校验前务必 trim() 用户输入,避免前后空格导致失败
必须启用 Session 并确认生效
验证码依赖 Session 存储原始值,TP6/TP8 默认禁用 Session 中间件,跳过这步,验证码永远不显示或始终校验失败。
- 打开 app/middleware.php,确保 \think\middleware\SessionInit::class 在数组中且未被注释
- 写个测试方法:
session('test', 'ok'); var_dump(session('test'));,输出 "ok" 才算成功 - 若用 Swoole 或 FastCGI 模式,还需确认 PHP 的 session.save_handler 配置正确(如 redis 或 files)
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











