如对接入流程存在疑问,参见快速接入


前端接入


web


兼容性


支持 Chrome、IE9+、360、腾讯、搜狗、Safari、Firefox、Opera;主流手机浏览器


引入初始化 SDK JS


<script src="https://www.yunpian.com/static/official/js/libs/riddler-sdk-0.2.2.js?t=20210804"></script>


注:IE9+需要在 SDK 之前额外引入 polyfill,示例如下


<script src="https://cdn.bootcss.com/babel-polyfill/7.4.3/polyfill.min.js"></script>


配置验证对象



new YpRiddler(options)
options 对象为配置对象,以下为配置参数:

参数

类型

必填

备注

onSuccess

function(validInfo:object, close:function)

Y

监听验证成功事件。validInfo:验证成功返回参数(token,authenticate);close:关闭验证窗口

appId

string

Y

应用标识。captchaId

version

string

Y

接口版本号

container

HTMLElement

Y

验证逻辑绑定的元素

noButton

boolean

F

是否在 container 内渲染按钮,当 mode 不为 flat 时有效

mode

string

F

UI 接入方式。flat-直接嵌入,float-浮动,dialog-对话框,external-外置滑动(拖动滑块时才浮现验证图片,仅适用于滑动拼图验证) 默认 dialog

onError

function

F

验证异常处理器。即当云片验证服务出现异常时,可以在此回调上处理,比如,不使用验证,或者,使用图片验证服务等。

onFail

function(code:int, msg:string, retry:function)

F

用户验证失败处理器, code: 错误码,msg: 错误信息,retry: 重试验证逻辑。默认实现为重新验证一次。

beforeStart

function(next:function)

F

进入验证逻辑前的勾子。next: 继续执行后续逻辑

expired

int

F

请求过期时限。单位秒,默认 30

jsonpField

string

F

jsonp 处理器名。默认为 ypjsonp

rsaPublicKey

string

F

加密公钥。如非异常情况则无需设置

hosts

string

F

验证服务器地址。如非异常情况则无需设置

winWidth

number/string

F

窗口宽度。宽度最小 230px: 两种方式:1. 纯数字格式,单位 px. 默认 500; 2. 百分比格式, 比如 80%,表示相对当前浏览器可视区域宽度的百分比。不小于 230px

lang

string

F

支持语言,默认简体中文。zh-cn(简体中文)、en(英文)

langPack

object

F

外部导入所需设置语言文案。需按指定格式设置对象导入,当外部导入语言包时,lang 设置会自动失效


winWidth 窗口宽度配置


// 设置窗口宽度为固定值(px)
new YpRiddler({
  ...
  winWidth: '500'
  ...
})

// 设置窗口宽度为屏幕宽度的百分比(窗口宽度winWidth = 屏幕宽度 * %)
new YpRiddler({
  ...
  winWidth: '30%'
  ...
})


lang 配置(可选)



系统默认支持中文,如需要替换其他语言请进行如下配置。目前支持的语言有:简体中文、英文。
如果需要设置文案的语言,可通过外部文件,按指定格式设置文案内容,然后在 options 配置项中通过 langPack 传入语言对象(object)即可。


// 语言模版
const LANG_OTHER = {
  'YPcaptcha_01': '请点击按钮开始验证',
  'YPcaptcha_02': '请按顺序点击:',
  'YPcaptcha_03': '向右拖动滑块填充拼图',
  'YPcaptcha_04': '验证失败,请重试',
  'YPcaptcha_05': '验证成功'
}
new YpRiddler({
  ...
  langPack: LANG_OTHER
  ...
})


Demo (Html)


<html>

<head>
    <!--依赖-->
    <script src="https://www.yunpian.com/static/official/js/libs/riddler-sdk-0.2.2.js?t=20210720"></script>
    <script>
        window.onload = function () {
            // 初始化
            new YpRiddler({
                expired: 10,
                mode: 'dialog',
                winWidth: 500,
                lang: 'zh-cn', // 界面语言, 目前支持: 中文简体 zh-cn, 英语 en
                // langPack: LANG_OTHER, // 你可以通过该参数自定义语言包, 其优先级高于lang
                container: document.getElementById('cbox'),
                appId: 'your-app-id',
                version: 'v1',
                onError: function (param) {
                        if (!param.code) {
                            console.error('错误请求');
                        }
                        else if (parseInt(param.code / 100) == 5) {
                            // 服务不可用时,开发者可采取替代方案,详见 “get 接口响应码释义”
                            console.error('验证服务暂不可用');
                        }
                        else if (param.code == 429) {
                            console.warn('请求过于频繁,请稍后再试');
                        }
                        else if (param.code == 403) {
                            console.warn('请求受限,请稍后再试');
                        }
                        else if (param.code == 400) {
                            console.warn('非法请求,请检查参数');
                        }
                    // 异常回调
                    console.error('验证服务异常')
                },
                onSuccess: function (validInfo, close, useDefaultSuccess) {
                    // 成功回调
                    alert('验证通过! token=' + validInfo.token + ', authenticate=' + validInfo.authenticate)
                    // 验证成功默认样式
                    useDefaultSuccess(true)
                    close()
                },
                onFail: function (code, msg, retry) {
                    // 失败回调
                    alert('出错啦:' + msg + ' code: ' + code)
                    retry()
                },
                beforeStart: function (next) {
                    console.log('验证马上开始')
                    next()
                },
                onExit: function () {
                    // 退出验证 (仅限dialog模式有效)
                    console.log('退出验证')
                }
            })
        }
    </script>
</head>

<body>
    <div id="cbox"></div>
</body>

</html>


Demo (React)


在@/public/index.html的header元素中加入:


<script src="https://cdn.bootcss.com/babel-polyfill/7.4.3/polyfill.min.js"></script>
<script src="https://www.yunpian.com/static/official/js/libs/riddler-sdk-0.2.2.js?t=20210720"></script>


使用方法如下:


import React from 'react'

// 如下配置仅作为示例,具体可参考'配置验证对象'小节
const initYpRiddler = () => {
  new window.YpRiddler({
    appId: '请在这里填入实际的appId',
    expired: 10,
    mode: 'dialog',
    winWidth: 500,
    lang: 'zh-cn',
    container: document.getElementById('cbox'),
    version: 'v1',
    onSuccess: function (validInfo, close, useDefaultSuccess) {
      alert(
        '验证通过! token=' +
          validInfo.token +
          ', authenticate=' +
          validInfo.authenticate
      )
      useDefaultSuccess.call(null, true)
      close()
    },
    onFail: function (code, msg, retry) {
      alert('出错啦:' + msg + ' code: ' + code)
      retry()
    },
    beforeStart: function (next) {
      console.log('验证马上开始')
      next()
    },
    onExit: function () {
      console.log('退出验证')
    },
  })
}

class App extends React.Component {
  componentDidMount() {
    initYpRiddler() // 在需要展示行为验证的时候,调用该方法
  }
  render() {
    <!-- id名称、样式均可根据需要进行设置 -->
    return <div id='cbox' style={{ width: '400px' }}></div>
  }
}

export default App


Demo (Vue)


在@/public/index.html的header元素中加入:


<script src="https://cdn.bootcss.com/babel-polyfill/7.4.3/polyfill.min.js"></script>
<script src="https://www.yunpian.com/static/official/js/libs/riddler-sdk-0.2.2.js?t=20210720"></script>


使用方法如下:


<template>
  <!-- id名称、样式均可根据需要进行设置 -->
  <div id="cbox" style="width: 300px"></div>
</template>

<script>
  export default {
    name: 'App',
    methods: {
      // 如下配置仅作为示例,具体可参考'配置验证对象'小节
      initYpRiddler() {
        new window.YpRiddler({
          appId: '6bb8b48f5e024ae3b7347be171b27ec9',
          expired: 10,
          mode: 'dialog',
          winWidth: 500,
          lang: 'zh-cn',
          container: document.getElementById('cbox'),
          version: 'v1',
          onSuccess: function (validInfo, close, useDefaultSuccess) {
            alert(
              '验证通过! token=' +
                validInfo.token +
                ', authenticate=' +
                validInfo.authenticate
            )
            useDefaultSuccess.call(null, true)
            close()
          },
          onFail: function (code, msg, retry) {
            alert('出错啦:' + msg + ' code: ' + code)
            retry()
          },
          beforeStart: function (next) {
            console.log('验证马上开始')
            next()
          },
          onExit: function () {
            console.log('退出验证')
          }
        })
      }
    },
    mounted() {
      this.initYpRiddler() // 在需要展示行为验证的时候,调用该方法
    }
  }
</script>


接入成功样例


前端接入完成后,通过谷歌浏览器的 Network 查看请求记录,verify 请求会返回两个参数:authenticatetoken


后端接入


接口名称


二次验证接口


接口地址


https://captcha.yunpian.com/v1/api/authenticate


请求


  • 请求方式:POST
  • 请求类型:application/x-www-form-urlencoded


请求参数

参数

类型

必填

备注

captchaId

string

Y

验证应用 id,对应用户后台分配的 APPID

token

string

Y

前端从 verfiy 接口获取的 token,token 作为一次验证的标志。

authenticate

string

Y

前端从 verfiy 接口验证通过后,返回的参数

secretId

string

Y

验证应用密钥 id

version

string

Y

版本,固定值 1.0

user

string

F

可选值,接入方用户标志,如担心信息泄露,可采用摘要方式给出。

timestamp

string

Y

当前时间戳的毫秒值,如 1541064141441

nonce

string

Y

随机正整数, 在 1-99999 之间,与 timestamp 配合可以防止消息重放

signature

string

Y

签名信息,见签名计算方法


支持的语言及请求示例


Java 请求示例


import java.io.IOException;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.PostMethod;

/**
 * A demo for YunPian CAPTCHA authenticate API
 */
public class AuthenticateDemo {

  private static String AUTH_URL = "https://captcha.yunpian.com/v1/api/authenticate";

  public static void main(String[] args) throws IOException, URISyntaxException {
    Map<String, String> paramMap = new HashMap<>();
    // replace the following "{example}"s with actual values
    paramMap.put("captchaId", "{APPID}");
    paramMap.put("secretId", "{secretId}");
    paramMap.put("token", "{token}");
    paramMap.put("authenticate", "{authenticate}");
    paramMap.put("version", "1.0");
    paramMap.put("timestamp", String.valueOf(System.currentTimeMillis()));
    paramMap.put("nonce", String.valueOf(new Random().nextInt(99999)));
    paramMap.put("user", "{user}"); // user is optional

    String signature = genSignature("{secretKey}", paramMap);
    paramMap.put("signature", signature);

    StringBuilder sb = new StringBuilder();
    PostMethod postMethod = new PostMethod(AUTH_URL);
    postMethod.addRequestHeader("Content-Type", "application/x-www-form-urlencoded");
    paramMap.forEach((k, v) -> {
      postMethod.addParameter(k, v);
    });
    HttpClient httpClient = new HttpClient();
    int status = httpClient.executeMethod(postMethod);
    String responseBodyAsString = postMethod.getResponseBodyAsString();
    System.out.println(responseBodyAsString);
  }

  // generate signature
  private static String genSignature(String secretKey, Map<String, String> params) {
    String[] keys = params.keySet().toArray(new String[0]);
    Arrays.sort(keys);
    StringBuilder sb = new StringBuilder();
    for (String key : keys) {
      sb.append(key).append(params.get(key));
    }
    sb.append(secretKey);
    return DigestUtils.md5Hex(sb.toString());
  }
}


C#请求示例


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Web;

namespace ConsoleApp1
{
    /// A demo for YunPian CAPTCHA authenticate API
    class AuthenticateDemo
    {
        protected const string AUTH_URL = "https://captcha.yunpian.com/v1/api/authenticate";
        protected const string VERSION = "1.0";
        protected const int MAX_NONCE = 99999;

        static void Main(string[] args)
        {
            Dictionary<string, string> parameters = new Dictionary<string, string>();
            // replace the following "{example}"s with actual values!!!
            parameters.Add("captchaId", "{APPID}");
            parameters.Add("secretId", "{secretId}");
            parameters.Add("token", "{token}");
            parameters.Add("authenticate", "{authenticate}");
            parameters.Add("version", VERSION);
            parameters.Add("timestamp", GetCurrentTimeMillis());
            parameters.Add("nonce", GetNonce().ToString());
            //parameters.Add("user", "{user}"); // user is optional

            // generate signature
            string sign = GenSignature("{secretKey}", parameters);
            parameters.Add("signature", sign);

            // authenticate
            string retString = PostAuthData(parameters);
            Console.WriteLine(retString);
        }

        // post authenticate data
        public static string PostAuthData(Dictionary<string, string> parameters)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var item in parameters)
            {
                if (sb.Length > 0)
                    sb.Append("&");
                sb.Append(item.Key + "=" + HttpUtility.UrlEncode(item.Value, System.Text.Encoding.UTF8));
            }
            string data = sb.ToString();

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(AUTH_URL);
            request.Timeout = 30 * 1000;

            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = Encoding.UTF8.GetByteCount(data);

            Stream myRequestStream = request.GetRequestStream();
            byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(data);
            myRequestStream.Write(requestBytes, 0, requestBytes.Length);
            myRequestStream.Close();

            HttpWebResponse response = (HttpWebResponse)request.GetResponse();
            Stream myResponseStream = response.GetResponseStream();
            StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.GetEncoding("utf-8"));
            string retString = myStreamReader.ReadToEnd();
            myStreamReader.Close();
            myResponseStream.Close();

            return retString;
        }
        // generate signature
        public static String GenSignature(String secretKey, Dictionary<String, String> parameters)
        {
            parameters = parameters.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value);
            StringBuilder builder = new StringBuilder();
            foreach (KeyValuePair<String, String> kv in parameters)
            {
                builder.Append(kv.Key).Append(kv.Value);
            }
            builder.Append(secretKey);
            String tmp = builder.ToString();
            MD5 md5 = new MD5CryptoServiceProvider();
            byte[] result = md5.ComputeHash(Encoding.UTF8.GetBytes(tmp));
            builder.Clear();
            foreach (byte b in result)
            {
                builder.Append(b.ToString("x2"));
            }
            return builder.ToString();
        }

        private static int GetNonce()
        {
            Random r = new Random();
            int n = r.Next(1, MAX_NONCE);
            return n;
        }

        private static String GetCurrentTimeMillis()
        {
            TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalMilliseconds).ToString();
        }
    }
}


PHP 请求示例


<?php
$params = array();
$params["authenticate"] ="{authenticate}";//用户验证通过后,返回的参数
$params["token"] ="{token}";//前端返回的 token
$params["captchaId"] ="{APPID}";//验证应用 id
$params["secretId"] ="{secretId}";//验证应用 secretId
$secretKey = "{secretKey}";//验证应用 secretKey
$params["version"] = "1.0";//版本,固定值1.0
$params["timestamp"] = sprintf("%d", round(microtime(true)*1000));// 当前时间戳的毫秒值,如1541064141441
$params["nonce"] = sprintf("%d", rand(1,99999)); //随机正整数, 在 1-99999 之间
ksort($params); // 参数排序
$buff="";

foreach($params as $key=>$value){
        $buff .=$key;
        $buff .=$value;
}
$buff .= $secretKey;
//print_r($buff);
$signature=md5($buff);
$params["signature"] =$signature ;//签名信息,见签名计算方法

$url="https://captcha.yunpian.com/v1/api/authenticate";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
/* 设置返回结果为流 */
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
/* 设置超时时间*/
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
/* 设置通信方式 */
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/x-www-form-urlencoded'));
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($params));
$result = curl_exec($ch);
var_dump($result);


Python 请求示例


import random
import time
import hashlib
import requests

authenticate = '{authenticate}'
secretKey = '{secretKey}'
token = '{token}'
captchaId = '{APPID}'
secretId = '{secretId}'

data = {
  'authenticate': authenticate,
  'captchaId': APPID,
  'nonce': str(random.randint(10000, 99999)),
  'secretId': secretId,
  'timestamp': str(time.time()).split('.')[0],
  'token': token,
  'version': '1.0'
}
print '%s: %s' % ('data', data)


sign_str = ''
items = sorted(data.items(), key=lambda d:d[0])

for item in items:
  sign_str += '%s%s' % (item[0],item[1])

sign_str += secretKey
print '%s: %s' % ('sign_str', sign_str)

signature = hashlib.md5(sign_str).hexdigest().lower()
data['signature'] = signature

print '%s: %s' % ('data', data)

url = 'https://captcha.yunpian.com/v1/api/authenticate'
headers = {'Content-Type': 'application/x-www-form-urlencoded'}

r = requests.post(url, data=data, headers=headers)

print r.text


补充说明:


1、签名计算方法


  1. 第一步:对所有请求参数(不包括 signature 参数),按照参数名 ASCII 码表升序顺序排序。如:foo=1, bar=2, foo_bar=3, baz=4 排序后的顺序是 bar=2, baz=4, foo=1, foo_bar=3 。
  1. 第二步:将排序好的参数名和参数值构造成字符串,格式为:key1+value1+key2+value2… 。根据上面的示例得到的构造结果为:bar2baz4foo1foo_bar3 。
  1. 第三步:选择与 secretId 配对的 secretKey ,加到上一步构造好的参数字符串之后,如 secretKey=e3da918313c14ea8b25db31f01263f80 ,则最后的参数字符串为  bar2barz4foo1foo_bar3e3da918313c14ea8b25db31f01263f80。
  1. 第四步:把 3 步骤拼装好的字符串采用 utf-8 编码,使用 MD5 算法对字符串进行摘要,计算得到 signature 参数值,将其加入到接口请求参数中即可。MD5 是 128 位长度的摘要算法,用 16 进制表示,一个十六进制的字符能表示 4 个位,所以签名后的字符串长度固定为 32 位十六进制字符。上述签名的结果为:59db908f26fb997c30b32ddb911485c2。


/**
 * 生成签名信息
 * @param secretKey 应用私钥
 * @param params 接口请求参数名和参数值map,不包括signature参数名
 * @return
 */
public static String genSignature(String secretKey, Map<String, String> params){
    // 1. 参数名按照ASCII码表升序排序
    String[] keys = params.keySet().toArray(new String[0]);
    Arrays.sort(keys);

    // 2. 按照排序拼接参数名与参数值
    StringBuilder sb = new StringBuilder();
    for (String key : keys) {
        sb.append(key).append(params.get(key));
    }
    // 3. 将secretKey拼接到最后
    sb.append(secretKey);

    // 4. MD5是128位长度的摘要算法,转换为十六进制之后长度为32字符
    return DigestUtils.md5Hex(sb.toString().getBytes("UTF-8"));
}


2、响应码释义


前端相关响应码


verify 接口响应码释义

响应码

错误信息

具体描述

0

ok

验证通过

1

bad request

验证请求数据缺失或格式有误

2

verify fail

验证不通过

400

param_invalid

请求参数错误,检查 i k 参数

400

captcha_id_invalid

APPID 不存在

429

too many requests

请求过于频繁,请稍后再试

500

server_error

服务异常


get 接口响应码释义

响应码

错误信息

具体描述

0

ok

获取验证图片成功

400

param_invalid

请求参数错误,检查 i k 参数

403

forbidden request

异常请求,已拦截,需要一段时间才允许恢复访问,可能是访问过于频繁导致

400

captcha_id_invalid

APPID 不存在

429

too many requests

请求过于频繁,请稍后再试

500

server_error

服务异常

531

server_reject

拒绝提供服务,例如验证量超过套餐额度后,可能会拒绝服务,同其他5xx错误码一样,应考虑回退方案


后端相关响应码


响应参数

参数

类型

必填

备注

code

int

Y

成功为 0,非 0 为异常信息,详见下方“二次验证接口响应码释义”

msg

string

Y

错误描述信息


二次验证接口响应码释义

响应码

错误信息

具体描述

0

ok

二次验证通过

400

validate_fail

二次验证不通过

400

signature_invalid

签名校验失败

400

param_invalid

请求参数错误,检查 i k 参数

400

captcha_id_invalid

APPID 不存在

400

re_authenticate

重复的authenticate和token

429

too many requests

请求过于频繁,请稍后再试

500

server_error

服务异常