常见问题
输入关键词快速定位问题,如「40103」「图片」「超时」。
接入后持续提示「验证已失效,请刷新后重试」410
错误现象
网站接入验证模块后,用户每次完成点选 / 滑块 / 旋转操作,提交都返回「验证已失效,请刷新后重试」,永远无法通过。
可能原因
- (V2 已修复)旧版组件跨站 Cookie 被拦截:挑战原绑定在 PHP 会话上,业务站点与验证服务不同源时,浏览器(Safari / Firefox / Chrome,以及 HTTP 站点 SameSite=Lax 场景)会拦截第三方会话 Cookie,check 请求拿不到会话即报 410;
- 使用了自研封装但未在 check 时回传
challengeId; - 同一 challengeId 在验证通过或达到 5 次尝试上限后重复提交(挑战一次性,终态后即销毁);
- 挑战生成后超过 180 秒才提交(有效期过期);
- check 的 type 与 get 领取的挑战类型不一致(如 get 为 click、check 按 slide 提交)。
解决方案
- 将前端 captcha.js 升级到 V2 版本:get 响应会下发
challengeId(服务端挑战存储标识),组件自动在 check 时回传,跨站接入不再依赖第三方 Cookie,本问题彻底解决; - 自研封装接入时,check 请求体必须原样携带 get 返回的
challengeId字段; - 每次验证使用最新领取的挑战,失败后重新 get,不要重放旧 challengeId;
- 确认 check URL 中的 type 与组件实际渲染的类型一致(官方组件会自动带上,无需手工处理)。
预防措施
- 优先使用官方 captcha.js 组件接入,组件已内置 challengeId 回传、失败自动刷新等完整逻辑;
- 灰度发布时先在 Safari 与 Chrome 无痕窗口(默认拦截第三方 Cookie)各跑通一次全流程;
- 保留会话回退仅用于同源场景,跨站接入务必确认 challengeId 机制已生效。
首页在线演示为什么需要登录?会消耗额度吗40104
错误现象
未登录访问首页时,演示区域显示「登录后即可在线体验」;或调用演示接口返回 40104「请先登录后再体验在线演示」。
可能原因
- 为防止演示资源被匿名脚本批量滥用,在线演示仅向注册用户开放,未登录时三个验证模块不会初始化,也不会发起任何接口请求;
- 登录会话空闲超过 30 分钟会自动失效,需要重新登录。
解决方案
- 登录或注册账号后即可使用全部三种验证模式的在线演示;
- 计费规则:首次成功验证免费(每个账号一次),之后每次成功验证消耗 1 次接口额度,验证失败不扣费,与正式接口计费模型一致;演示区会实时显示剩余额度;
- 开发与联调若不想消耗额度,可改用演示接口
https://safe.yunzhiapi.cn/validate/captcha.php(会话制、不计费,验证通过仅返回一次性会话凭证,不签发正式 token,无法用于 tokenVerify)。
预防措施
- 正式接入始终以
api/captcha.php+ tokenVerify 为准,演示仅用于体验与联调; - 在用户中心关注剩余额度,避免演示与测试消耗影响生产调用。
验证码图片加载失败,显示空白或裂图
错误现象
点击验证按钮后弹窗内图片区域空白,浏览器控制台显示图片请求 500 或直接失败。
可能原因
- PHP 未启用 GD 扩展,无法生成验证图片;
- GD 扩展未编译 webp 支持,缓存目录中的 .webp 底图无法读取;
- 字体文件缺失(validate/font/ 目录为空或无读权限),文字绘制失败;
- validate/cache/、validate/image/ 目录不存在或 Web 服务器用户无读写权限。
解决方案
- 检查 GD 及 webp 支持:
<?php
var_dump(extension_loaded('gd'));
$info = gd_info();
var_dump(!empty($info['WebP Support'])); // 需为 true
- 确认
validate/font/下存在 .ttf 字体且权限可读;确认validate/cache/可写; - 查看 PHP error_log 中的具体报错(如
imagecreatefromwebp(): is not a valid WebP file)。
预防措施
- 部署上线前运行环境自检脚本,确认 GD/webp/字体/目录权限四项全部通过;
- 升级 PHP 后重新执行自检,扩展配置可能随版本变化。
网络超时或请求失败
错误现象
前端验证弹窗长时间转圈后提示失败;或服务端 cURL 调用 tokenVerify 超时。
可能原因
- 服务器到验证接口之间网络不通(防火墙、出口限制);
- 页面是 HTTPS 而接口走 HTTP,被浏览器按混合内容拦截;
- 反向代理(Nginx 等)超时时间设置过短,长请求被提前切断。
解决方案
- 前端 fetch 设置合理超时并给出重试入口(组件默认 15 秒超时);
- 全站统一 HTTPS,避免 http/https 混用;
- 服务端调用设置连接 3 秒、整体 5 秒超时,并准备降级策略(详见开发文档「服务端结果处理」)。
预防措施
- 上线前在服务器上用 curl 实测接口连通性与耗时:
curl -o /dev/null -s -w "%{http_code} %{time_total}s\n" https://safe.yunzhiapi.cn/api/captcha.php
- 为验证失败准备兜底文案,避免用户面对无响应的页面。
参数错误40001
错误现象
调用接口返回 40001「参数错误:缺少必要参数或格式不正确」。
可能原因
- 缺少必填字段(如 tokenVerify 少了 appSecret);
- 以 GET 方式提交本应收 POST 的接口,参数未被读取;
- Content-Type 不对,服务端解析不到表单字段。
解决方案
错误与正确写法对比:
// 错误:GET 提交、参数拼 URL
curl_setopt($ch, CURLOPT_URL, $url . '?appId=' . $appId);
// 正确:POST + application/x-www-form-urlencoded
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query([
'appId' => $appId,
'appSecret' => $appSecret,
'token' => $token,
]));
预防措施
- 对照开发文档参数表逐项核对字段名(大小写敏感,是
appId不是appid); - 封装统一的调用函数,避免各处手工拼参数。
来源非法 / Origin 校验不通过40301
错误现象
正式接口返回 40301「请求来源非法」,或浏览器报跨域错误。
可能原因
- 平台配置了来源白名单(环境变量
YZ_ALLOWED_ORIGINS),而发起验证页面的 Origin 不在名单内; - 页面嵌在第三方 iframe 中,顶层来源与白名单域名不同;
- 本地 file:// 打开页面调试,Origin 为 null。
解决方案
- 将业务站点的完整 Origin(协议 + 域名 + 端口,如
https://shop.example.com)加入白名单并重启 PHP 服务:
YZ_ALLOWED_ORIGINS=https://shop.example.com,https://m.example.com
- 校验逻辑说明:服务端读取
HTTP_ORIGIN与白名单精确比对,不一致即返回 40301;未配置白名单时保持开放(与旧版行为一致); - 本地调试请通过本地 Web 服务访问(如 http://localhost),不要直接双击打开 html 文件。
预防措施
- 配置域名时把测试环境与生产环境域名都纳入,避免环境切换后 40301;
- 不要把接口地址暴露给无关站点嵌入使用。
session、Cookie 与 SameSite 问题
错误现象
跨站嵌入页面中验证状态丢失;或浏览器控制台提示 Cookie 因 SameSite 被阻止。
可能原因
- 旧版本挑战绑定在 PHP 会话 Cookie 上,跨站嵌入时会话 Cookie 被 SameSite 策略或浏览器第三方 Cookie 策略拦截(V2 起正式接口已改为 challengeId 服务端存储,不再有此问题);
- 演示接口
validate/captcha.php(会话制,不计费,不签发正式 token)仍依赖会话 Cookie,仅限同源调试使用; - 会话接口与页面协议不一致(http/https 混用)导致 Secure Cookie 无法写入;
- 平台会话生命周期为 1800 秒(与挑战 180 秒、token 300 秒的有效期相匹配),登录态空闲超时后需重新登录。
解决方案
- 正式接入统一使用
https://safe.yunzhiapi.cn/api/captcha.php?appId=...凭证制接口 + 最新 captcha.js:挑战经 challengeId 与浏览器会话解耦,Safari / 微信内置浏览器等拦截第三方 Cookie 的环境也能正常工作; - 演示 / 调试请保持同源访问,或改用正式接口(扣额度但行为与生产一致);
- 确需保留会话绑定的私有化场景,全站 HTTPS 后会话 Cookie 自动设置为
SameSite=None; Secure(亦可用环境变量YZ_SESSION_SAMESITE显式控制)。
预防措施
- 接入方案设计时优先采用 challengeId 机制,仅把会话绑定当作同源回退;
- 跨站场景务必在 Chrome、Safari、微信内置浏览器分别实测。
组件升级后,旧的接入代码会受影响吗
错误现象
看到平台发布组件更新后,担心已上线的接入代码需要改造,或旧版组件突然不可用。
可能原因
- 误解了版本策略:平台组件升级遵循完全向后兼容原则,接口地址、请求字段与回调约定保持不变;
- 旧版组件仍可正常工作,但无法获得新版的安全增强能力。
解决方案
- 无需改造现有接入代码:appId / apiServer / success / fail 等初始化参数与 token 校验流程完全不变,V2.2 升级同样遵循该承诺;
- 建议将页面引用的 captcha.js / captcha.css 升级为最新版本组件,以获得完整的防护能力与兼容性修复(如跨站 Cookie 被拦截场景的 challengeId 机制);
- 升级后按开发文档「接入检查清单」回归验证一次全流程即可。
预防措施
- 关注平台公告,组件更新后择机升级,保持使用最新版本;
- 灰度发布时先在测试环境验证,再推全量。
升级后验证图片的视觉样式发生变化,是否正常
错误现象
平台升级到 V2.2 后,滑块的拼图块边缘变得柔和、凹槽与背景的融合度更高,点选图中的文字颜色不再那么鲜艳、且出现了提示中没有的相似文字,用户或运营同事疑惑是否异常。
可能原因
- 属正常现象:V2.2 对验证图像做了同源化安全增强——滑块拼图块与背景噪声统计一致、边缘渐变羽化,凹槽以同亮度随机纹理填充并叠加全局边缘噪声,真实凹槽与干扰凹槽外观完全同源;
- 点选图中字符颜色采样自背景局部并自然融合,目标文字逐字采用不同字号与随机倾角,同时混入未出现在提示中的形近干扰文字;
- 这些变化均为提升机器识别难度的对抗设计,人眼辨认与正常操作不受影响,交互方式与判定逻辑与之前完全一致。
解决方案
- 无需任何处理:按原方式正常操作即可通过——滑块沿水平轨道将拼图移至轮廓吻合的凹槽处,点选按提示图文字顺序点击图中相同文字;
- 无需修改任何接入代码,接口字段与回调约定均未变化。
预防措施
- 可在客服话术与用户引导中说明「验证图样式可能随安全策略升级微调」,减少用户疑惑。
验证挑战失效或过期41041001
错误现象
用户打开验证弹窗后停留较久才操作,或重复提交同一次挑战,返回「验证已失效,请刷新后重试」或「验证已过期,请重新验证」(引擎码 410;API 层对应 41001)。
可能原因
- 挑战有效期为 180 秒,用户停留超时;
- 挑战为一次性:验证通过、达到 5 次尝试上限或过期后即销毁,重放旧 challengeId / 旧会话挑战必然 410;
- challengeId 绑定了签发端 IP/UA,在代理切换、VPN 或更换浏览器后提交会因环境不匹配而失效(V2 新增防护);
- 旧版组件跨站接入时第三方 Cookie 被拦截(详见「验证已失效排查」条目,V2 已修复);
- 多机负载均衡部署且使用文件兜底存储时,生成挑战与校验挑战落在不同服务器(建议使用 MySQL 存储后端,天然跨机共享)。
解决方案
- 官方组件已在收到 410 后自动重新领取挑战,用户无感;自研封装请实现同样的自动刷新逻辑;
- 确保平台库(safe_yunzhiapi)连接正常,挑战存储优先走 MySQL,多机部署天然共享;
- 前端在验证通过 / 失败后及时销毁旧挑战状态,不要缓存复用。
预防措施
- 在弹窗内做挑战倒计时提示,临近过期主动刷新;
- 定期检查
yz_challenge表体积,过期记录会被自动清理,无需人工干预。
token 校验失败40103
错误现象
前端已拿到 token,但服务端调用 tokenVerify 返回 40103「token 无效或已过期」。
可能原因
- token 超过 300 秒有效期:用户验证通过后长时间未提交表单;
- token 被重复使用:刷新页面后拿旧 token 再次提交,或重试逻辑重放了已消费的 token;
- token 与 appId 不匹配:前端使用的 AppID 与服务端 tokenVerify 使用的 AppID 不一致。
解决方案
- 收到 40103 时引导用户重新验证获取新 token,业务侧不要自动重放旧 token;
- 检查前后端 AppID 是否一致,特别是多环境(测试/生产)配置串用的情况;
- 表单提交按钮点击后应禁用,防止用户双击产生重复提交。
预防措施
- 在 token 即将过期时(如验证通过 240 秒后)提示用户重新验证;
- 每次表单提交都使用最新的 onSuccess 回调 token,不要跨页面缓存 token。
AppID / AppSecret 无效4010140102
错误现象
接口返回 40101「AppID 不存在」或 40102「AppSecret 校验失败」。
可能原因
- 复制凭证时带入了首尾空格或换行;
- AppSecret 被重置过,业务侧仍使用旧密钥;
- 把 AppID 与 AppSecret 位置填反,或使用了别的账号的凭证。
解决方案
- 到用户中心重新复制完整凭证:AppID 为 32 位十六进制,AppSecret 为 64 位十六进制;
- 服务端读取凭证后先 trim 再使用:
$appId = trim($config['appId']);
$appSecret = trim($config['appSecret']);
if (!preg_match('/^[0-9a-f]{32}$/', $appId)) {
// AppID 格式有误,检查配置
}
- 怀疑密钥泄露时,在用户中心点击「重置密钥」并同步更新所有服务端配置。
预防措施
- 凭证只保存在服务端配置文件或环境变量中,不进入版本库;
- 重置密钥建立变更流程,避免多环境只更新了一部分。
余额不足40201
错误现象
正式接口调用返回 40201「接口调用额度不足」,验证无法完成。
可能原因
- 账户剩余调用额度已消耗至 0;
- 流量突增(活动期间)消耗速度超出预期;
- 误将高频内部测试流量打到正式接口,浪费了额度。
解决方案
- 登录用户中心查看剩余额度并及时充值;
- 额度扣减时机说明:按成功验证计费——获取挑战免费,仅验证通过时原子扣减一次接口调用额度,验证失败不扣费,余额为 0 时服务中止;
- 开发与联调请改用演示接口
https://safe.yunzhiapi.cn/validate/captcha.php(会话制,不计费,验证通过仅返回一次性会话凭证,不签发正式 token);
预防措施
- 在用户中心定期关注余额,活动前预估流量提前充值;
- 服务端捕获 40201 时给出明确告警,而不是让错误穿透到终端用户。
请求过于频繁42901
错误现象
短时间内连续调用接口,返回 42901「请求过于频繁」。
可能原因
- 触发了接口限流策略,单位时间请求数超过阈值;
- 前端存在循环调用或重复初始化的 bug;
- 重试逻辑没有间隔,失败后立即重发形成放大效应。
解决方案
采用指数退避重试,示例:
function retryCall($fn, $maxRetry = 3) {
for ($i = 0; $i <= $maxRetry; $i++) {
$resp = $fn();
if (($resp['code'] ?? 0) !== 42901) {
return $resp;
}
sleep(min(pow(2, $i), 8)); // 1s、2s、4s……封顶 8 秒
}
return $resp;
}
- 排查前端是否有 setInterval 定时拉取、重复 create 实例等问题。
预防措施
- 客户端对同一用户的验证发起做节流(如 2 秒内只允许一次);
- 压测时提前评估限流阈值,避免压测流量触发限流影响真实用户。
担心 SQL 注入与数据安全
错误现象
安全审计时质疑:用户输入的用户名、token 是否可能注入数据库。
可能原因
- 历史代码存在字符串拼接 SQL 的写法;
- 第三方接入方在自己系统中手工拼接 token 入库;
- 误将未过滤的输入直接输出到页面,引入 XSS 风险。
解决方案
- 本平台所有数据库访问均使用 PDO 预处理语句(
prepare+execute),不存在拼接 SQL; - 接入方在自己的业务代码中同样必须使用预处理,不要拼接 token、用户名等外部输入:
// 正确
$stmt = $pdo->prepare('SELECT id FROM user WHERE name = ?');
$stmt->execute([$username]);
// 错误(严禁)
$pdo->query("SELECT id FROM user WHERE name = '$username'");
- 输出到 HTML 的变量一律经
htmlspecialchars转义。
预防措施
- 代码评审中将「拼接 SQL」列为红线项;
- 数据库账号按最小权限授权,只授予所需库的读写权限。
点击 / 滑块 / 旋转验证一直失败
错误现象
用户确认操作正确,但验证反复提示失败要求重试。
可能原因
- 点选采用动态容差:点击偏离目标文字中心过远,或落在了相邻文字附近,会被判定失败;
- 点选必须严格按提示顺序,三个目标全部命中但顺序错位同样判失败;
- 滑块需把拼图沿水平轨道移动到轮廓吻合的凹槽处:目标凹槽与拼图处于同一水平线上,图中可能出现多个外观相似的干扰凹槽,对齐到错误凹槽会失败;
- 旋转图片可能被随机水平/垂直镜像翻转:未先点击翻转按钮还原镜像就直接旋转,角度再准也无法通过;
- 高分辨率屏(设备像素比 devicePixelRatio > 1)下坐标未做换算——组件已内置换算,若自行魔改前端需注意;
- 触屏设备上手指按压偏移,小屏手机上目标区域过小;
- 操作过快、节奏机械或缺乏真实操作特征,被多维行为特征模型判定为机器操作(V2.2 起行为轨迹深度校验覆盖速度分布、轨迹形态、加速度、时间规律、轨迹熵值与操作延迟等维度),应放慢速度、以自然节奏手动重试,勿使用自动化工具或连点器。
解决方案
- 引导用户尽量点准目标文字中心、拼图对齐同形状凹槽、镜像图片先翻转再转正后松手;
- 不要修改组件的坐标采集与上报逻辑,dpr 换算已内置;
- 移动端确保 viewport 正确设置:
<meta name="viewport" content="width=device-width, initial-scale=1.0">。
预防措施
- 高频场景优先选 click 模式,交互容差更友好;高风险场景再选 slide / rotate;
- 上线前在真实手机(含高 dpr 机型)上完整走一遍验证流程。
旋转验证为什么要先点击「翻转」按钮
错误现象
用户把图片旋转到了正方向,提交后仍提示验证失败。
可能原因
- V2 起旋转验证的图片除随机角度旋转外,还会随机叠加水平 / 垂直镜像翻转(4 种组合),用于扩大解空间、抵御角度遍历攻击;
- 镜像翻转无法通过旋转还原:左右 / 上下颠倒的图片旋转到任何角度都不是原图。
解决方案
- 观察图片是否为镜像(如左右颠倒、上下颠倒):先点击验证图右上角的「水平翻转」「垂直翻转」按钮还原镜像;
- 再拖动滑块把图片旋转至正方向后松手提交;
- 翻错按钮可以再点一次取消,两个按钮可独立开关。
预防措施
- 业务方在用户引导文案中提示「镜像图片请先翻转再旋转」,可降低首次失败率;
- 高风险场景建议保留该模式,镜像 + 旋转双因子对机器攻击的拦截强度显著高于纯旋转。
高并发下验证响应变慢
错误现象
活动高峰期验证图片加载变慢,接口耗时从几十毫秒升到秒级。
可能原因
- 每个请求都实时生成图片,CPU 被打满;
- 数据库缺少索引,凭证查询、日志写入拖慢整体;
- PHP-FPM 进程数不足,请求排队。
解决方案
- 利用平台内置的图片缓存机制:底图预生成在
validate/cache/,命中缓存时无需实时绘图,确认缓存目录可写且空间充足; - 为核心表加索引(username、app_id、token、created_at):
ALTER TABLE app_credential ADD INDEX idx_app_id (app_id);
ALTER TABLE call_log ADD INDEX idx_user_time (username, created_at);
- 适当调大 PHP-FPM 的 pm.max_children,并开启 OPcache。
预防措施
- 活动前压测验证接口,确认缓存命中率与 QPS 上限;
- 慢查询日志常态化监控,索引随数据量增长复核。
登录被锁定,提示账号已锁定
错误现象
登录页提示「账号已锁定,请 N 分钟后再试」。
可能原因
- 同一账号连续 5 次密码错误,触发安全锁定策略;
- 密码被他人尝试爆破导致无辜锁定;
- 忘记密码反复试错。
解决方案
- 锁定规则:连续失败 5 次锁定 30 分钟,倒计时结束后自动解除,无需人工干预;
- 等待解锁后使用正确密码登录;解锁成功登录后失败计数自动清零;
- 确认非本人操作触发锁定的,建议登录后立即修改密码。
预防措施
- 使用密码管理器保存密码,避免反复试错;
- 不要将账号借给他人使用。
为什么不同时间验证的难度不一样
错误现象
同一用户在不同时间访问,有时一次即通过,有时挑战看起来略复杂,甚至需要多试一次。
可能原因
- 这是平台的动态风控引擎在起作用:系统会结合当前访问环境的风险状况实时评估风险等级,自动调节挑战强度与判定策略。难度评估以服务端行为历史为主——历史通过率、失败率、异常请求统计(如只取不验)、会话风险等级与 IP 请求压力;客户端上报的环境信号仅作辅助参考,权重有限,伪造客户端参数无法主导难度判定;
- 风险较高的时段或环境下,挑战会相应加强;环境可信、历史通过率良好时则保持轻量验证。
解决方案
- 属正常现象,无需处理:对正常用户而言挑战仍以无感、轻量为主,按提示正常操作即可通过;
- 请勿使用自动化脚本、模拟器等工具访问,这类环境会持续触发高强度挑战且难以通过。
预防措施
- 在用户引导文案中说明「验证难度可能随安全策略动态调整」,降低用户困惑;
- 高风险业务场景(抢购、领券等)可在用户中心配置更严格的验证模式,与动态风控形成叠加防护。