如对接入流程存在疑问,参见快速接入
前端接入
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 | 服务异常 |