首页 验证体验 登录演示 注册演示 接入文档 定价方案 登录

接入文档

将枢爻验证码集成到你的网站,只需 3 步,5 分钟搞定。

接入流程

整个接入过程分为前端后端两部分,前端负责展示验证码,后端负责校验结果:

1
前端引入 SDK
在你的页面引入 CSS + JS
2
用户完成验证
SDK 弹出验证码,用户操作通过后获得 token
3
前端提交 token
把 token 随表单一起发给你的后端
4
后端校验 token
你的后端调用验证码服务确认 token 有效

⚠️ 重要:token 必须在你的后端校验,不能只在前端判断。前端回调只代表用户操作完成,不代表安全可信。

3 步快速接入

第 1 步 前端:引入 SDK 并触发验证

在你的 HTML 页面中加入以下代码。YOUR_SERVER 替换为验证码服务的地址,YOUR_APP_ID 替换为在管理后台创建的应用 ID。

HTML
<!-- 1. 引入验证码样式 -->
<link rel="stylesheet" href="https://YOUR_SERVER/sdk/shuyao-captcha.css">

<!-- 2. 你的提交按钮 -->
<button id="submitBtn">提交</button>

<!-- 3. 初始化验证码 -->
<script type="module">
  import { ShuyaoCaptcha } from 'https://YOUR_SERVER/sdk/shuyao-captcha.js';

  const captcha = ShuyaoCaptcha.init({
    serverUrl: 'https://YOUR_SERVER',  // 验证码服务地址
    appId:     'YOUR_APP_ID',          // 应用 ID
    onSuccess(token) {
      // 用户验证通过!把 token 发给你的后端
      fetch('/api/login', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ captchaToken: token })
      });
    }
  });

  // 点击按钮弹出验证码
  document.getElementById('submitBtn').addEventListener('click', () => {
    captcha.show();
  });
</script>

第 2 步 后端:校验 token

你的后端收到前端提交的 token 后,调用验证码服务的 /api/v1/captcha/validate 接口确认是否有效(需携带 appId 和 appKey 鉴权):

你的后端 → 验证码服务
// 请求
POST https://YOUR_SERVER/api/v1/captcha/validate
Content-Type: application/json

{
  "appId":  "YOUR_APP_ID",
  "appKey": "YOUR_APP_KEY",
  "token":  "用户提交的 token 值"
}

// 响应(统一 ApiResponse 格式)
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "a1b2c3d4e5f67890",
  "data": {
    "valid": true   ← true 表示验证通过,可以继续业务
  }
}

💡 token 是一次性的,校验后立即失效。有效期 300 秒。validate 接口需要 appKey 鉴权,请妥善保管密钥。

第 3 步 根据校验结果处理业务

伪代码
function handleLogin(request) {
  // 1. 拿到前端提交的 token
  token = request.body.captchaToken

  // 2. 调用验证码服务校验(需携带 appId + appKey)
  result = httpPost("https://YOUR_SERVER/api/v1/captcha/validate", {
    appId:  "YOUR_APP_ID",
    appKey: "YOUR_APP_KEY",
    token:  token
  })

  // 3. 判断是否通过(检查统一返回的 success 和 data.valid)
  if (!result.success || !result.data.valid) {
    return error("验证码校验失败")
  }

  // 4. 验证通过,继续你的正常业务逻辑
  doLogin(request.body.username, request.body.password)
}

就这么简单!下面是详细的 API 参考和各语言的示例代码。

POST /api/v1/captcha/init — 初始化会话

POST /api/v1/captcha/init

SDK 内部自动调用。生成会话 ID、ECDH 密钥对、PoW 挑战参数。

请求体

字段类型必填说明
appIdString你的应用 ID(在管理后台创建)
clientInfoObject客户端环境信息,由 SDK 自动采集
clientInfo.fingerprintString设备指纹
clientInfo.webdriverBoolean是否检测到 WebDriver
clientInfo.headlessBoolean是否为无头浏览器

响应体

JSON
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "a1b2c3d4e5f67890",
  "data": {
    "sessionId":       "550e8400e29b41d4a716446655440000",
    "serverPublicKey":  "MFkwEwYHKoZIzj0C...",      // ECDH 公钥
    "powDifficulty":    16,                           // PoW 难度
    "powPrefix":        "550e8400...:a1b2c3d4...",   // PoW 前缀
    "timestamp":        1682406316000                  // 服务器时间戳
  }
}

POST /api/v1/captcha/challenge — 获取验证挑战

POST /api/v1/captcha/challenge

SDK 内部自动调用。根据风险评估生成验证挑战(图形推理或轨迹追踪)。

请求体

字段类型必填说明
sessionIdString从 init 接口获得的会话 ID
clientPublicKeyString客户端 ECDH 公钥(启用加密时需要)
environmentDataObject环境检测数据
preferredTypeString指定挑战类型:TRANSFORMATION 或 TRAJECTORY

响应体

JSON
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "b2c3d4e5f6789012",
  "data": {
    "challengeId": "abc123def456...",
    "type":        "TRANSFORMATION",    // 或 TRAJECTORY
    "renderData":  { ... },               // 渲染数据(可能加密)
    "encrypted":   false                   // 是否已加密
  }
}

POST /api/v1/captcha/verify — 提交验证

POST /api/v1/captcha/verify

SDK 内部自动调用。提交用户答案、行为数据,进行防重放、PoW、答案、行为四重验证。

请求体

字段类型必填说明
sessionIdString会话 ID
challengeIdString挑战 ID
answerString用户答案
behaviorDataObject行为采集数据(鼠标事件、耗时等)
powNonceStringPoW 计算结果
timestampLong客户端时间戳
nonceString请求唯一标识(防重放)

响应体

JSON - 成功
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "c3d4e5f678901234",
  "data": {
    "success":   true,
    "token":     "NTUwZTg0MDAtZTI5Yi00MWQ0...",  // 验证 Token
    "message":   "验证通过",
    "riskLevel": "LOW"                              // 风险等级
  }
}
JSON - 失败
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "d4e5f67890123456",
  "data": {
    "success":   false,
    "token":     null,
    "message":   "验证失败,请重试",
    "riskLevel": "MEDIUM"
  }
}

POST /api/v1/captcha/refresh — 刷新验证码

POST /api/v1/captcha/refresh

当用户需要更换一道验证题目时调用,SDK 内部在用户点击"换一个"时自动触发。会废弃当前挑战并生成新的挑战数据。

请求体

字段类型必填说明
sessionIdString当前会话 ID

响应体

JSON
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "e5f6789012345678",
  "data": {
    "challengeId": "new_challenge_id...",
    "type":        "TRANSFORMATION",
    "renderData":  { ... },
    "encrypted":   false
  }
}

💡 如果会话已过期(返回错误码 1002),需要重新调用 init 接口创建新会话。

POST /api/v1/captcha/cancel — 关闭会话

POST /api/v1/captcha/cancel

主动关闭会话,释放服务端资源(Redis 中的会话数据)。SDK 在用户关闭验证弹窗时自动调用。

请求体

字段类型必填说明
sessionIdString要关闭的会话 ID

响应体

JSON
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "f6789012345678ab",
  "data":      null
}

POST /api/v1/captcha/validate — 校验 Token

POST /api/v1/captcha/validate

这是你需要在自己后端调用的唯一接口。其他接口都由 SDK 自动处理。此接口需要 appKey 鉴权。

🔒 此接口应由你的服务器调用,不要在前端 JavaScript 中直接调用。Token 一次性使用,验证后立即失效。请妥善保管 appKey,不要泄露到前端代码中。

请求体

字段类型必填说明
appIdString你的应用 ID
appKeyString你的应用密钥(在管理后台获取)
tokenString用户验证通过后获得的 token 字符串
JSON 请求示例
{
  "appId":  "shuyao_abc123def456",
  "appKey": "sk_live_xyz789uvw...",
  "token":  "NTUwZTg0MDAtZTI5Yi00MWQ0..."
}

响应体

JSON - 验证通过
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "a1b2c3d4e5f67890",
  "data": {
    "valid": true,
    "appId": "shuyao_abc123def456"
  }
}
JSON - 验证失败
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "a1b2c3d4e5f67890",
  "data": {
    "valid": false,
    "appId": "shuyao_abc123def456"
  }
}
JSON - 鉴权失败
{
  "success":   false,
  "code":      "1006",
  "message":   "appKey错误",
  "requestId": "a1b2c3d4e5f67890",
  "data":      null
}

GET /api/v1/captcha/health — 健康检查

GET /api/v1/captcha/health

检查验证码服务是否正常运行。可用于监控和运维探活。无需鉴权。

响应体

JSON
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "0a1b2c3d4e5f6789",
  "data": {
    "status":  "UP",
    "version": "1.0"
  }
}

统一返回格式

所有 API 接口均采用统一的 ApiResponse 包装格式返回,方便前后端统一处理:

JSON
{
  "success":   true,           // 请求是否成功
  "code":      "1000",         // 业务错误码
  "message":   "成功",          // 提示信息
  "requestId": "a1b2c3d4...",  // 请求追踪 ID(全链路唯一)
  "data":      { ... }          // 业务数据(失败时可能为 null)
}
字段类型说明
successBooleantrue 表示请求处理成功,false 表示出现错误
codeString业务错误码,"1000" 为成功,其他为对应错误
messageString人类可读的提示信息
requestIdString本次请求的唯一追踪 ID,可用于排查问题
dataObject实际业务数据,结构因接口不同而异

💡 requestId 是全链路追踪标识。当出现问题时,提供 requestId 可以帮助快速定位服务端日志。

错误码说明

所有接口共用以下错误码体系。当 successfalse 时,根据 code 判断具体错误类型:

错误码含义说明
1000成功请求处理成功
1001参数错误请求参数缺失或格式不正确
1002会话已过期sessionId 无效或已超时,需重新调用 init
1003验证失败用户提交的答案不正确
1004请求过于频繁触发频率限制,请稍后再试
1005服务异常服务端内部错误,请联系管理员
1006鉴权失败appKey 错误或应用已禁用(validate 接口)
1007IP 已被封禁当前 IP 触发风控规则被临时封禁
JSON - 错误响应示例
{
  "success":   false,
  "code":      "1002",
  "message":   "会话已过期",
  "requestId": "f6789012345678ab",
  "data":      null
}

引入 SDK

SDK 由一个 CSS 文件和一个 JS 模块组成,无任何第三方依赖。

HTML
<!-- 引入样式 -->
<link rel="stylesheet" href="https://YOUR_SERVER/sdk/shuyao-captcha.css">

<!-- 引入 JS(ES Module) -->
<script type="module">
  import { ShuyaoCaptcha } from 'https://YOUR_SERVER/sdk/shuyao-captcha.js';
</script>

💡 把 YOUR_SERVER 替换为你部署验证码服务的域名或 IP,例如 https://captcha.example.com。如果通过 Nginx 反向代理访问,直接用你的网站域名即可。

配置参数

参数类型必填默认值说明
serverUrlString验证码后端 API 地址(如 https://captcha.example.com
appIdString应用 ID(在管理后台「应用管理」中创建获取)
themeString'light'主题样式:'light'(浅色)或 'dark'(深色)
onSuccessFunction验证成功回调,参数为 token 字符串
onFailFunction验证失败回调,参数为失败原因字符串
onCloseFunction用户关闭验证码弹窗时触发

方法与回调

方法

方法说明
ShuyaoCaptcha.init(config)初始化,传入配置,返回实例
实例.show()弹出验证码窗口,开始验证
实例.destroy()销毁实例,移除所有 DOM 和事件

回调事件

回调触发时机参数你需要做什么
onSuccess用户验证通过token(String)把 token 发给你的后端校验
onFail验证失败message(String)提示用户重试
onClose用户关闭弹窗可选处理

完整前端示例

一个登录页面的完整接入示例:

HTML
<!DOCTYPE html>
<html>
<head>
  <link rel="stylesheet" href="https://YOUR_SERVER/sdk/shuyao-captcha.css">
</head>
<body>
  <form id="loginForm">
    <input name="username" placeholder="用户名">
    <input name="password" type="password" placeholder="密码">
    <button type="button" id="loginBtn">登录</button>
  </form>

  <script type="module">
    import { ShuyaoCaptcha } from 'https://YOUR_SERVER/sdk/shuyao-captcha.js';

    const captcha = ShuyaoCaptcha.init({
      serverUrl: 'https://YOUR_SERVER',
      appId: 'YOUR_APP_ID',
      onSuccess(token) {
        const form = document.getElementById('loginForm');
        fetch('/api/login', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            username: form.username.value,
            password: form.password.value,
            captchaToken: token
          })
        }).then(r => r.json())
          .then(data => {
            if (data.success) alert('登录成功');
            else alert(data.message || '登录失败');
          });
      },
      onFail(msg) { alert('验证失败: ' + msg); }
    });

    document.getElementById('loginBtn').addEventListener('click', () => {
      captcha.show();
    });
  </script>
</body>
</html>

Java / Spring Boot 后端校验

Java
@PostMapping("/api/login")
public ResponseEntity<?> login(@RequestBody LoginRequest req) {
    // 1. 向验证码服务校验 token(需携带 appId + appKey)
    RestTemplate rest = new RestTemplate();
    Map<String, String> body = Map.of(
        "appId",  "YOUR_APP_ID",
        "appKey", "YOUR_APP_KEY",
        "token",  req.getCaptchaToken()
    );
    Map result = rest.postForObject(
        "https://YOUR_SERVER/api/v1/captcha/validate", body, Map.class);

    // 2. 检查统一返回格式
    if (!(Boolean) result.get("success")) {
        return ResponseEntity.badRequest().body(result.get("message"));
    }
    Map data = (Map) result.get("data");
    if (!(Boolean) data.get("valid")) {
        return ResponseEntity.badRequest().body("验证码校验失败");
    }

    // 3. 验证通过,处理登录逻辑...
    return ResponseEntity.ok("登录成功");
}

Node.js / Express 后端校验

JavaScript
app.post('/api/login', async (req, res) => {
  // 1. 向验证码服务校验 token(需携带 appId + appKey)
  const resp = await fetch('https://YOUR_SERVER/api/v1/captcha/validate', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      appId:  'YOUR_APP_ID',
      appKey: 'YOUR_APP_KEY',
      token:  req.body.captchaToken
    })
  });
  const result = await resp.json();

  if (!result.success || !result.data?.valid) {
    return res.status(400).json({ error: result.message || '验证码校验失败' });
  }

  // 2. 验证通过,处理登录逻辑...
  res.json({ success: true });
});

Python / Flask 后端校验

Python
@app.route('/api/login', methods=['POST'])
def login():
    token = request.json.get('captchaToken')

    # 1. 向验证码服务校验 token(需携带 appId + appKey)
    resp = requests.post(
        'https://YOUR_SERVER/api/v1/captcha/validate',
        json={
            'appId':  'YOUR_APP_ID',
            'appKey': 'YOUR_APP_KEY',
            'token':  token
        }
    )
    result = resp.json()

    if not result.get('success') or not result.get('data', {}).get('valid'):
        return jsonify(error=result.get('message', '验证码校验失败')), 400

    # 2. 验证通过,处理登录逻辑...
    return jsonify(success=True)

PHP 后端校验

PHP
// 1. 向验证码服务校验 token(需携带 appId + appKey)
$token = $_POST['captchaToken'];
$ch = curl_init('https://YOUR_SERVER/api/v1/captcha/validate');
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
    'appId'  => 'YOUR_APP_ID',
    'appKey' => 'YOUR_APP_KEY',
    'token'  => $token
]));
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);

if (!$result['success'] || !$result['data']['valid']) {
    die($result['message'] ?? '验证码校验失败');
}

// 2. 验证通过,处理登录逻辑...

GET /api/v1/admin/stats — 统计数据

GET /api/v1/admin/stats

获取系统运行统计:验证总量、通过率、每日趋势、类型分布、风险分布。需要管理员鉴权。

JSON 响应
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "admin_req_001",
  "data": {
    "totalVerifyCount":  10000,
    "passCount":         8500,
    "blockCount":        1500,
    "passRate":          85.0,
    "todayVerifyCount":  1200,
    "todayPassCount":    1020,
    "todayBlockCount":   180,
    "dailyStats": {
      "2026-04-15": 1100,
      "2026-04-16": 1200, ...
    },
    "typeStats": {
      "TRANSFORMATION": 6000,
      "TRAJECTORY": 4000
    },
    "riskStats": {
      "LOW": 6500, "MEDIUM": 2000,
      "HIGH": 1000, "CRITICAL": 500
    }
  }
}

GET /api/v1/admin/logs — 验证日志

GET /api/v1/admin/logs

分页查询验证日志,支持按状态和 IP 过滤。需要管理员鉴权。

查询参数

参数类型默认值说明
pageInteger1页码
sizeInteger20每页条数
statusString过滤状态:passed(通过)/ blocked(拦截)
ipString按 IP 地址模糊搜索

响应

JSON
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "admin_req_002",
  "data": {
    "logs": [{
      "id": 1,
      "timestamp": 1682406316000,
      "clientIp": "192.168.1.100",
      "challengeType": "TRANSFORMATION",
      "passed": true,
      "riskScore": 25,
      "duration": 3500,
      "sessionId": "550e8400..."
    }],
    "total": 10000,
    "page": 1,
    "size": 20
  }
}

应用管理 API

GET /api/v1/admin/appkeys — 获取应用列表

JSON 响应
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "admin_req_003",
  "data": [{
    "appId":        "shuyao_abc123def456",
    "appKey":       "sk_live_xyz789uvw...",
    "appName":      "我的网站",
    "status":       "ENABLED",
    "createdAt":    1682406316000,
    "monthlyQuota": 100000,
    "usedCount":    12345,
    "domains":      ["localhost", "*.example.com"]
  }]
}

POST /api/v1/admin/appkeys — 创建新应用

JSON 请求
{
  "appName": "我的网站",
  "domains": ["localhost", "*.mysite.com"]  // 可选,默认 ["localhost"]
}

POST /api/v1/admin/appkeys/{appId}/reset — 重置密钥

重新生成 appKey,旧密钥立即失效。响应返回新的 appKey。

POST /api/v1/admin/appkeys/{appId}/toggle — 启用/禁用

JSON 请求
{ "enabled": true }  // true=启用, false=禁用

PUT /api/v1/admin/appkeys/{appId}/domains — 更新授权域名

JSON 请求
{ "domains": ["localhost", "*.newdomain.com"] }

DELETE /api/v1/admin/appkeys/{appId} — 删除应用

永久删除应用,操作不可撤销。

风控配置 API

GET /api/v1/admin/riskconfig — 获取配置

JSON 响应
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "admin_req_004",
  "data": {
    "ipRateLimit":          true,
    "fingerprintDetect":    true,
    "headlessDetect":       true,
    "webdriverDetect":      true,
    "powEnabled":           true,
    "adaptiveDifficulty":   true,
    "encryptTransport":     true,
    "antiReplay":           true,
    "maxRequestsPerMinute": 30,
    "powDefaultDifficulty": 16,
    "powHighDifficulty":    20
  }
}

PUT /api/v1/admin/riskconfig — 更新配置

请求体结构与 GET 响应的 data 字段相同,修改后实时生效。

DELETE /api/v1/admin/cache/clear — 清除缓存

DELETE /api/v1/admin/cache/clear

清除 Redis 中所有 shuyao:* 前缀的缓存数据。清除后会话和挑战需重新生成。

JSON 响应
{
  "success":   true,
  "code":      "1000",
  "message":   "成功",
  "requestId": "admin_req_005",
  "data": {
    "message": "Redis 缓存已清除"
  }
}

加密通信

系统使用 ECDH(椭圆曲线 Diffie-Hellman) 协商共享密钥,通过 AES-GCM 加密验证数据传输,防止中间人攻击。整个过程由 SDK 自动完成,无需额外配置。

参数
椭圆曲线secp256r1 (P-256)
对称加密AES-GCM(128 位 Tag)
IV 长度12 字节(随机)
共享密钥导出ECDH 原始密钥 → SHA-256 → 32 字节

风控策略

系统根据 IP 频率、客户端环境、设备指纹三个维度综合评估风险,自动选择挑战类型和难度:

风险等级评分范围挑战类型PoW 难度
低风险0 - 29爻变推演(简单)16 位
中风险30 - 59爻变推演(中等)16 位
高风险60 - 84轨迹密钥(复杂)20 位
极高风险85+复合验证20 位

安全增强特性

系统实现了多层安全加固机制,保护验证流程免受各类攻击:

requestId 全链路追踪

每个 API 请求都会分配唯一的 requestId,贯穿整个请求生命周期。通过 requestId 可以在服务端日志中精确追踪一次请求的完整链路,方便问题排查和审计。

Token 指纹绑定

验证通过后生成的 Token 会绑定用户的 IP 地址设备指纹。在后端调用 validate 校验时,系统会验证请求来源是否与 Token 绑定的信息一致,防止 Token 被盗用或跨设备转移。

防重放增强

安全特性参数说明
Nonce 去重窗口5 分钟同一 nonce 在 5 分钟内不可重复使用,防止请求重放
时间戳容许偏差±2 分钟客户端与服务器时间差超过 2 分钟的请求将被拒绝
Token 一次性使用Token 校验后立即失效,不可重复使用

💡 这些安全机制由系统自动执行,接入方无需额外配置。SDK 已内置时间戳和 nonce 的自动生成逻辑。