如对接入流程存在疑问,参见快速开始
名词解释
术语 | 描述 |
---|---|
验证码业务id | captchaId(appId), 验证码唯一标识,公开可见,用于区分不同的验证码使用场景,如登录、投票、发帖等。可在云片用户后台验证服务产品管理页面进行创建管理。 |
密钥对id | Secret Id, 请求密钥id,与Secret Key配对使用,用于对二次校验接口参数进行签名计算。 |
密钥对Key | Secret Key, 请求密钥Key, 与密钥id配对使用,用于对二次校验接口参数进行签名计算。 |
验证 | 用户拖动/点击一次验证码拼图即视为一次“验证”,不论拖动/点击是否正确。 |
二次校验 | 验证数据随表单提交到产品后台后,产品后台需要将验证数据发送到云片验证服务后台做二次校验,目的是核实验证数据的有效性。 |
验证码类型 | 滑动拼图、文字点选等,在云片用户后台产品管理页面可以创建并修改 |
前端接入
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"></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': '验证成功',
'YPcaptcha_06': '验证失败'
}
new YpRiddler({
...
langPack: LANG_OTHER
...
})
Demo
<html>
<head>
<!--依赖-->
<script src="https://www.yunpian.com/static/official/js/libs/riddler-sdk-0.2.2.js"></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 == 429) {
alert('请求过于频繁,请稍后再试!')
return
}
// 异常回调
console.error('验证服务异常')
},
onSuccess: function (validInfo, close) {
// 成功回调
alert(`验证通过!token=${validInfo.token}, authenticate=${validInfo.authenticate}`)
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>
接入成功样例
前端接入完成后,通过谷歌浏览器的Network查看请求记录,verify请求会返回两个参数:authenticate和token。
后端接入
接口名称
二次验证接口
接口地址
https://captcha.yunpian.com/v1/api/authenticate
请求
- 请求方式:POST
- 请求类型:application/x-www-form-urlencoded
请求参数
参数 | 类型 | 必填 | 备注 |
---|---|---|---|
captchaId | string | Y | 验证产品 id |
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", "{captchaId}");
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", "{captchaId}");
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"] ="{captchaId}";//验证产品 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 = '{captchaId}'
secretId = '{secretId}'
data = {
'authenticate': authenticate,
'captchaId': captchaId,
'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 | captchaId不存在 |
429 | too many requests | 请求过于频繁,请稍后再试 |
500 | server_error | 服务异常 |
get接口响应码释义
响应码 | 错误信息 | 具体描述 |
---|---|---|
0 | ok | 获取验证图片成功 |
400 | param_invalid | 请求参数错误,检查i k参数 |
400 | captcha_id_invalid | captchaId不存在 |
429 | too many requests | 请求过于频繁,请稍后再试 |
500 | server_error | 服务异常 |
后端相关响应码
响应参数
参数 | 类型 | 必填 | 备注 |
---|---|---|---|
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 | captchaId不存在 |
429 | too many requests | 请求过于频繁,请稍后再试 |
500 | server_error | 服务异常 |