如对接入流程存在疑问,参见快速接入
前端接入
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 请求会返回两个参数:authenticate和token。
后端接入
接口名称
二次验证接口
接口地址
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、签名计算方法
- 第一步:对所有请求参数(不包括 signature 参数),按照参数名 ASCII 码表升序顺序排序。如:foo=1, bar=2, foo_bar=3, baz=4 排序后的顺序是 bar=2, baz=4, foo=1, foo_bar=3 。
- 第二步:将排序好的参数名和参数值构造成字符串,格式为:key1+value1+key2+value2… 。根据上面的示例得到的构造结果为:bar2baz4foo1foo_bar3 。
- 第三步:选择与 secretId 配对的 secretKey ,加到上一步构造好的参数字符串之后,如 secretKey=e3da918313c14ea8b25db31f01263f80 ,则最后的参数字符串为 bar2barz4foo1foo_bar3e3da918313c14ea8b25db31f01263f80。
- 第四步:把 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 | 服务异常 |