验证码坐标校验失败主因是前后端坐标未对齐:后端需存汉字四元组坐标并确保session正常,前端须用getBoundingClientRect处理缩放,移动端应改用touchstart并设足够热区容错。

ThinkPHP 验证码点击汉字选字时坐标传不回后台
后端生成的验证码图片里汉字位置是随机的,但前端点击后传回来的 x、y 坐标没对齐——常见原因是图片缩放没处理。浏览器渲染时,<img> 标签设置了 width/height 属性或 CSS 缩放,但 Canvas 或事件坐标仍按原始尺寸计算。
- 用
getBoundingClientRect()获取真实渲染区域,再减去offsetLeft/offsetTop,别直接用event.clientX/clientY - 后端生成验证码时必须记录每个汉字的原始坐标(如
['一' => [20, 45], '二' => [80, 45]]),并存入 session 或加密透传到前端(如 base64 编码后塞进data-coords) - ThinkPHP 的
think-captcha默认不支持坐标存储,得重写Captcha类的generate()方法,在$this->code之外额外写入$this->positions
前端点击汉字后如何匹配后端预存坐标
不能靠像素级比对,汉字区域有宽高(比如 40×40),得做「点击点是否落在某矩形内」的判断。关键不是算距离,而是定义好「可点击热区」的半径或范围。
- 后端存坐标时,建议统一存为
[x, y, width, height]四元组,比如['一' => [15, 40, 30, 30]],前端校验时用clickX >= x && clickX 这类区间判断 - ThinkPHP 接收请求后,先解密/还原坐标数据(如从
$_POST['coords']中取),再遍历比对;别把坐标存在 hidden input 里明文传,容易被篡改 - 注意中文字符顺序:后端生成的汉字串(如
一三五七)和前端点击顺序必须严格一致,否则位置索引错位。建议后端返回一个word_order字段标明显示顺序
ThinkPHP 6 验证失败总提示“验证码错误”但看不出哪步错了
默认错误信息太笼统,实际可能是坐标解析失败、session 丢失、汉字匹配越界或加密解密不一致。得一层层打日志,而不是只看最终返回值。
前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。
- 在
CaptchaService::check()开头加Log::info('captcha check raw:', ['post' => request()->param(), 'session' => Session::get('captcha')]); - 检查
Session::get('captcha')是否为空——TP6 默认用file驱动时,如果runtime/session目录不可写,session 就存不住,导致每次校验都查不到原始坐标 -
captcha_id参数必须传且和服务端生成时一致;若用了多实例(如不同模块不同验证码),要确保id不冲突,否则取错 session key
移动端点击偏移大,汉字选不准
手机端 touch 事件的 clientX/clientY 在某些机型上会因 viewport 缩放、字体渲染差异产生几像素偏差,而汉字热区又小,容易误判。
立即学习“PHP免费学习笔记(深入)”;
- 别只监听
click,改用touchstart并阻止默认行为(event.preventDefault()),避免延迟和双击缩放干扰 - 热区宽度别设太小,至少留出
20px容错,比如汉字本身宽 24px,热区设成40px × 40px - ThinkPHP 后端校验时加个「最近邻容错」逻辑:如果点击点没落在任何热区内,计算它到各中心点的欧氏距离,取最小的那个(
sqrt((x1-x2)^2 + (y1-y2)^2)),但仅限距离< 25才接受


















