- 名词解释
- 前端接入
- web端
- 兼容性
- 引入初始化SDK JS
- 配置验证对象
- winWidth窗口宽度配置
- lang配置(可选)
- Demo
- 接入成功样例
- web端
- 后端接入
- 接口名称
- 接口地址
- 请求
- 请求参数
- 支持的语言及请求示例
- Java请求示例
- C#请求示例
- PHP请求示例
- Python请求示例
- 补充说明:
- 1、签名计算方法
- 2、响应码释义
- 前端相关响应码
- verify接口响应码释义
- get接口响应码释义
- 后端相关响应码
- 响应参数
- 二次验证接口响应码释义
如对接入流程存在疑问,参见快速开始
名词解释
| 术语 | 描述 |
|---|---|
| 验证码业务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, // 你可以通过该参数自定义语言包, 其优先级高于langcontainer: 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 valuesparamMap.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 optionalString 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 signatureprivate 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 APIclass 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 signaturestring sign = GenSignature("{secretKey}", parameters);parameters.Add("signature", sign);// authenticatestring retString = PostAuthData(parameters);Console.WriteLine(retString);}// post authenticate datapublic 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 signaturepublic 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 randomimport timeimport hashlibimport requestsauthenticate = '{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 += secretKeyprint '%s: %s' % ('sign_str', sign_str)signature = hashlib.md5(sign_str).hexdigest().lower()data['signature'] = signatureprint '%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 | 服务异常 |
