文档中心
产品简介
拖动式人机验证:滑块拼图 / 角度旋转 / 曲线重合 / 点击验证 / 文字点选 / 字体选择六种方式,验证图形由服务端生成、答案不出服务端; 配合工作量证明、行为轨迹分析、环境指纹复核与全流程风控,机器脚本难以批量通过。 支持内联与弹窗两种接入形态(弹窗为独立面板,六种方式通用),并检测 WebDriver / 开发者工具等调试环境后立即终止验证。 自托管部署、无第三方依赖、数据完全自有。
快速接入
- 控制台创建应用(选择验证方式),得到
app_id/app_secret(secret 只放业务后端)。 - 页面引一个入口(apiBase 与 load.js 均使用完整绝对地址;支持跨域接入,服务端 CORS 已放开。接入页与验证服务同域时,域名部分可省略为根绝对路径
/captcha/…):<script>window.DinoCaptchaConfig = { apiBase: 'https://你的验证域名/captcha/api/', appId: '你的app_id' };</script> <script src="https://你的验证域名/captcha/load.js"></script> <div id="box" data-dino-captcha></div>加载期间容器自动显示占位条,组件就绪后自动装配。组件所有请求(init / 分段传输 / 拼图 / pow.wasm)都基于 apiBase 解析,不要写相对路径。
- 业务后端核验(用户验证通过拿到 token 后,登录等强安全场景建议加
once:true一次性核验):POST https://你的验证域名/captcha/api/open-verify.php { "app_id": "…", "app_secret": "…", "token": "…", "once": true } // → 200 { code:200, app:"应用名", expires_in:0, consumed:true }
验证方式(控制台按应用选择,随时切换)
| 方式 | 交互 | 判定 |
|---|---|---|
| 滑块拼图 | 图片碎片乱序 + 缺口滑块 | 位置 ±5px + 行为轨迹 + 环境 |
| 角度旋转 | 服务端旋转后的圆形图,拖回正向 | 角度模 360,容差 ±12° |
| 曲线重合 | 滑块平移曲线族,与图中弧线重合 | 顶点位置 ±6px |
| 点击验证 | 单击验证条,WASM POW 直达 | POW + 环境指纹 |
| 点击验证(弹窗) | 独立面板:上方加载动画 + 下方「验证中…」,完成直接显示验证成功 | POW + 环境指纹 |
| 文字点选 | 按提示顺序点击图中 3 个目标文字 | 坐标 ±22px + 点选节奏 |
| 字体选择 | 点击与其它字形样式不同的那个字 | 坐标 ±24px + 节奏 |
所有方式均叠加:行为轨迹分析、环境指纹复核、全流程风控(频次 / 失败 / 指纹 / 请求头)。
调试环境检测:识别到 WebDriver 自动化标记,或开发者工具(F12 / Ctrl+Shift+I·J·C、面板停靠导致的窗口尺寸差)时,
验证立即终止——清空 token、停止后续请求并提示「检测到调试环境,验证已终止」,失败自动重验同样被禁用;
服务端将 webdriver / devtools 标记作为一票否决项。检测过程不阻塞主线程,
iframe 内嵌场景不会误判。
外观(毛玻璃 / 浅色 / 深色)在控制台按应用设置,随资源清单下发、装配时一次成型;
未绑定 appId 的页面可用 DinoCaptchaConfig.theme 指定。
前端:入口与配置
所有配置统一在 load.js 加载:先读 load.js 内置默认值 → 再拉取云端配置(api/client-config.php)→
最后叠加页面 window.DinoCaptchaConfig。运行中还可调用 DinoCaptcha.configure({…}) 局部更新。
| 配置项 | 说明 |
|---|---|
| apiBase | 完整绝对地址(如 https://域名/captcha/api/);缺省取 load.js 所在目录的 api/。支持跨域,组件全部请求基于它解析 |
| appId | 应用标识(控制台创建);绑定后验证方式与配置以应用为准 |
| mode | 未绑定 appId 时的展示方式提示(slider / rotate / curve / click / text / font) |
| theme | 未绑定 appId 时的外观提示(light / dark / glass);绑定 appId 后以控制台应用配置为准 |
| popup.mode | 弹出式默认验证方式;留空且未绑定 appId 时自动用 click(单击直达) |
| popup.title | 弹窗标题,默认「安全验证」 |
| popup.autoVerify | 打开弹窗即使用独立面板自动开始(默认 true);设 false 则退回「先显示验证条、点击后再验」 |
| popup.closeDelay / popup.timeout | 成功后自动关闭延迟(默认 1000ms,留出成功动画时间)/ 弹窗最长存活(默认 300000ms) |
| cloud | 设 false 关闭云配置拉取(纯本地配置) |
站点自用页面(官网/演示/控制台)通过 api/site-config.php 统一注入绝对 apiBase 与导航链接基准,
站点域名由 config.php 的 site_domain(或环境变量 DINO_SITE_DOMAIN)设置,留空 = 同域根绝对路径。
云端配置
load.js 启动时先 GET /captcha/api/client-config.php(6s 超时,失败静默降级为本地默认值),
取回 { code:200, config:{…} } 合并后继续装配。优先级:load.js 默认值 < 云端配置 < 页面 DinoCaptchaConfig。
// 控制台 API 写入云端配置(全站即时生效,无需改文件) POST /captcha/api/console.php { "action":"setclientcfg", "admin_key":"…", "config":{ "mode":"click", "theme":"light", "popup":{"title":"请完成安全验证","autoVerify":true,"closeDelay":800} } } // 清除云端覆盖,回落到 config.php { "action":"delclientcfg", "admin_key":"…" }
云端配置只下发白名单字段(mode / theme / appId / title / popup),密钥类字段永不下发。
也可在 config.php 写死 'client_config' => [...],作为云端未配置时的兜底。
弹出式验证与事件绑定
三种调用方式,均可读取返回 Promise 的 {pass, token, reason}:
// ① 声明式:load.js 就绪后自动绑定,点击按钮弹窗 <button data-dino-captcha="popup" data-dino-mode="click">安全验证</button> // ② 一键核验:弹出并立即开始(click 模式直接跑 POW → verify-final) DinoCaptcha.verify().then(r => { if (r.pass) console.log(r.token); }); // ③ 事件绑定:任意元素 → 弹窗,回调/事件拿到 token DinoCaptcha.bind('#submit-btn', { mode:'click', title:'请完成安全验证', onSuccess: token => {}, onFail: d => {}, onClose: r => {} });
实例与弹窗事件:DinoCaptcha.on('success'|'fail'|'open'|'shrink'|'popup-open'|'popup-close', fn),
同时派发同名 dinocaptcha:* DOM 事件(detail 含 token / pass / reason / container)。
弹窗支持全部六种验证方式
滑块拼图 / 角度旋转 / 曲线重合 / 点击验证 / 文字点选 / 字体选择均可在弹窗中运行, 验证流程与内联组件完全一致(含失败自动重验、重试/关闭按钮、移动端等比缩小)。
弹窗使用独立 UI 面板,不嵌入内联验证条、不显示组件边框,也没有展开/收缩动画: 打开弹窗立即呈现固定尺寸的面板(常规 340×288,文字/字体 340×340)。
弹窗实例不会预先加载挑战:页面加载与点击「登录」之前不产生 init / POW 请求,
pow.wasm 与 vendor 依赖也延迟到弹窗首次打开时才加载(纯内联页面则在启动阶段加载);
只有弹窗真正打开后才开始核验。面板内不显示组件自带的关闭控制按钮,统一使用弹窗右上角的 × 关闭。
加载与重试使用统一的 SVG 圆环动画:内联验证条在点击验证进行中显示小号圆环, 弹窗首次加载与面板内「刷新」手动重试显示同款大号圆环 + 状态文案;成功后统一播放「空心圆 + 勾」动画。
| 验证方式 | 弹窗内的呈现 |
|---|---|
| 点击验证 | 整个弹窗就是核验界面:上方 SVG 圆环加载动画 + 下方「验证中…」;完成后同一区域直接播放「空心圆 + 勾」成功动画并显示「验证成功」(失败显示「空心圆 + 叉」与重试提示) |
| 滑块 / 旋转 / 曲线 / 文字 / 字体 | 先显示 SVG 圆环与「验证初始化」,挑战就绪后在同一尺寸面板内直接呈现,无展开过渡;验证通过后显示「验证成功」并播放「空心圆 + 勾」成功动画 |
设置 autoVerify:false 可退回「先显示验证条、点击后再验证」的传统形态。
| 方法 | 说明 |
|---|---|
| DinoCaptcha.popup(opts) | 弹出独立面板并自动开始,返回 Promise;opts 同配置区 popup |
| DinoCaptcha.verify(opts) | 同 popup(自动核验),返回 Promise |
| DinoCaptcha.bind(el, opts) | 元素点击绑定弹窗;返回 {open, close, on, destroy} |
| DinoCaptcha.unbind(el) | 解绑元素 |
| DinoCaptcha.closePopup() | 关闭当前弹窗(按 reason='api' 结束 Promise) |
| DinoCaptcha.configure(patch) | 运行时局部更新配置(深合并) |
| DinoCaptcha.mount(el) | 手动挂载内联组件(返回实例) |
| DinoCaptcha.getToken() / reset() | 读取当前 token / 触发重新验证 |
| captcha.open / verify / refresh / close / reset / getToken / on / off / destroy | 实例级便捷方法(window.captcha) |
状态变量与事件
window.captcha.container // 容器名 window.captcha.isPass // 是否验证成功(true 后可读 token) window.captcha.token // 业务 token(验证成功后出现,供后端 open-verify 二次核验;重新验证/失败时清空) window.captcha.again = true // 触发重新验证(token 同时清空) window.captcha.open() / verify() / refresh() / close() / reset() / getToken() // 实例便捷方法(内部方法名每代随机,这里用稳定外壳驱动) window.captcha.on('success'|'fail'|'open'|'shrink', fn) // 实例事件,返回取消函数 window.IsPass // 与 captcha.isPass 同步的便捷全局 document.addEventListener('dinocaptcha:ready', …) // 组件装配完成 document.addEventListener('dinocaptcha:error', …) // 装载失败(e.detail 为错误对象) document.addEventListener('dinocaptcha:success', …) // 验证通过(detail.token) document.addEventListener('dinocaptcha:fail', …) // 本次验证失败 document.addEventListener('dinocaptcha:popup-open', …) / 'dinocaptcha:popup-close' // 弹窗打开 / 关闭(close 含 reason,可为 success/button/mask/escape/timeout/blocked) document.addEventListener('dinocaptcha:blocked', …) // 检测到调试环境,验证已终止(detail.reason: webdriver/f12/devtools…)
典型取用:前端验证通过后把 captcha.token 随业务表单提交给你的后端,后端调开放 API 核验,token 不得只在前端自证。
开放 API(服务端调用)
POST /captcha/api/open-verify.php · Content-Type: application/json · 仅业务后端调用(app_secret 不进前端)。
请求参数
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| app_id | string | 是 | 应用标识(控制台创建) |
| app_secret | string | 是 | 应用密钥(仅业务后端持有) |
| token | string | 是 | 前端验证通过得到的业务 token(captcha.token) |
| once | bool | 否 | 是否一次有效:传 true 时校验成功后立即核销 token,同一 token 再次校验返回 404。缺省 false(有效期内可重复校验)。登录、支付、改密等强安全场景建议传 true。 |
响应(200)
| 字段 | 说明 |
|---|---|
| code | 200 |
| app | 应用名称 |
| created_at | token 签发时间戳(秒) |
| expires_in | 剩余有效秒数(token 全生命周期 1 小时);once 核销后为 0 |
| consumed | true 表示本次校验已核销该 token(once=true 时) |
错误码
| HTTP | code | 含义 |
|---|---|---|
| 400 | 400 | 参数缺失(app_id / app_secret / token) |
| 403 | 403 | 应用不存在或已停用;应用密钥错误;token 与应用不匹配 |
| 404 | 404 | token 无效或已过期(含 once 核销后重复校验) |
| 503 | 503 | 存储服务不可用 |
调用示例
# cURL:一次性核验(推荐登录场景)
curl -s -X POST https://your-domain/captcha/api/open-verify.php \
-H "Content-Type: application/json" \
-d '{"app_id":"你的app_id","app_secret":"你的app_secret","token":"前端传来的token","once":true}'
// PHP $ch = curl_init('https://your-domain/captcha/api/open-verify.php'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'app_id' => $appId, 'app_secret' => $appSecret, 'token' => $_POST['captcha_token'], 'once' => true, ]), CURLOPT_RETURNTRANSFER => true, ]); $res = json_decode(curl_exec($ch), true); if ($res['code'] === 200) { // 人机验证通过,继续业务 }
once:true 使 token 首次核验即失效,杜绝 token 重放,适合登录/下单/改密等一次性动作。两种模式按请求选择,同一应用可混用。控制台 API
POST /captcha/api/console.php · Content-Type: application/json · 除 login 外均需 admin_key(config.php 或环境变量 DINO_ADMIN_KEY;留空则控制台停用,返回 503)。
login(登录,需人机验证)
{ "action": "login", "admin_key": "…", "captcha_token": "前端人机验证 token" }
captcha_token 为控制台页人机验证通过的业务 token,一次性核销:无论密钥对错都消耗,每次尝试都需重新完成人机验证(防密钥爆破)。 返回 200 登录通过;403 未完成人机验证;401 管理密钥错误。
管理动作
| action | 附加参数 | 说明 / 成功响应 |
|---|---|---|
| list | — | 应用列表(含统计与最近日志)→ {apps:[…]} |
| create | name, domain?, mode?, theme? | 创建应用 → {app_id, app_secret}(secret 仅此一次展示;theme: light/dark/glass) |
| setmode | id, mode | 切换应用验证方式(slider/rotate/curve/click/text/font) |
| settheme | id, theme | 切换应用外观(light 浅色 / dark 深色 / glass 毛玻璃),即时生效 |
| getclientcfg | — | 读取 load.js 云配置({config, source}) |
| setclientcfg | config 或 mode/theme/title/popup | 写入云端展示配置,全站即时生效(白名单字段,密钥永不下发) |
| delclientcfg | — | 清除云端覆盖,回落到 config.php |
| toggle | id, enabled | 启用 / 停用应用 |
| delete | id | 删除应用(其历史 token 同步失效) |
| stats | id | 应用统计与最近 50 条验证记录 → {stat_*, logs} |
配置参考(config.php)
| 键 | 默认 | 说明 |
|---|---|---|
| storage | mysql | mysql / redis |
| mysql.* | — | 连接信息;表自动创建 |
| segment_count | 3 | 并发传输段数 |
| pow_difficulty | 5 → 6 | 工作量证明强度区间 |
| verify_mode | slider | 默认验证方式(未绑定应用时) |
| redis_host / redis_port | 127.0.0.1 / 6379 | storage=redis 时的连接 |
| risk | enabled | 风控:qps_per_ip / init_per_min / attempts_10m / fails_10m / fp_per_ip / initonly_gap / whitelist(支持 1.2.3.* 前缀) |
| prepic_dir / prepic_target | null / 300 | AI 素材池目录与目标数量 |
| zhipu_api_key | '' | AI 素材预生成 |
| admin_key | '' | 控制台密钥;留空停用 |
| site_domain | '' | 站点域名(如 https://cdn.dinonode.org):页面导航与组件 apiBase 生成为绝对地址;留空 = 同域根绝对路径。环境变量 DINO_SITE_DOMAIN 可覆盖 |
| node_bin | '' | node 路径(留空自动探测) |
| client_config | [] | 云端配置兜底(load.js 拉取 api/client-config.php 时使用;控制台 setclientcfg 优先级更高) |
部署
- 上传 captcha 目录;PHP ≥7.4 + redis/gd/curl/openssl 扩展;MySQL 建库
captcha。 - config.php 填 mysql 连接与 admin_key;npm install(启用深度混淆,可选)。
- 按需调整禁用函数(proc_open / popen)或使用计划任务补素材。
- Nginx 屏蔽内部目录;自检脚本:
php tools/check_env.php。
常见问题
- 资源加载 503:存储不可用——检查数据库配置与服务状态。
- 分块 410:资源版本已轮换,刷新页面即可。
- 提交 400 校验失败:页面缓存了旧版本资源,强制刷新。
- 验证方式不生效:绑定 appId 后以控制台应用配置为准;未绑定页可用
DinoCaptchaConfig.mode提示。 - open-verify 返回 404:token 过期(1 小时),或此前已用
once:true核销过——让前端重新验证拿新 token。 - 控制台登录 403「请先完成人机验证」:未完成验证或验证 token 已被上一次尝试消耗,重新完成人机验证再登录。
- 同一 token 第二次 open-verify 失败:首次调用带了
once:true,属预期行为;需要多次确认就不要传 once。 - 跨域接入:apiBase 与 load.js 都写验证服务的完整域名(https://开头),服务端 CORS 已放开全部来源;接入页不要使用相对路径。
- 页面显示「检测到调试环境,验证已终止」:浏览器开启了开发者工具(F12 / Ctrl+Shift+I·J·C)、面板处于停靠状态、断点挂起,或页面受 WebDriver 自动化控制。关闭工具并刷新页面即可恢复;这是预期行为,不是故障。
- 弹窗里看不到验证条 / 没有展开动画:弹窗使用独立面板(默认
autoVerify:true),不嵌入验证条、无边框与展开动画;需要传统形态时设popup.autoVerify:false。