如對接入流程存在疑問,參見快速接入
前端接入
web
兼容性
支持 Chrome、IE9+、360、騰訊、搜狗、Safari、Firefox、Opera;主流手機瀏覽器
引入初始化 SDK JS
<script src="http://www.ssjdyy2.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 對象為配置對象,以下為配置參數(shù):
參數(shù) | 類型 | 必填 | 備注 |
onSuccess | function(validInfo:object, close:function) | Y | 監(jiān)聽驗證成功事件。validInfo:驗證成功返回參數(shù)(token,authenticate);close:關(guān)閉驗證窗口 |
appId | string | Y | 應用標識。captchaId |
version | string | Y | 接口版本號 |
container | HTMLElement | Y | 驗證邏輯綁定的元素 |
noButton | boolean | F | 是否在 container 內(nèi)渲染按鈕,當 mode 不為 flat 時有效 |
mode | string | F | UI 接入方式。flat-直接嵌入,float-浮動,dialog-對話框,external-外置滑動(拖動滑塊時才浮現(xiàn)驗證圖片,僅適用于滑動拼圖驗證) 默認 dialog |
onError | function | F | 驗證異常處理器。即當云片驗證服務(wù)出現(xiàn)異常時,可以在此回調(diào)上處理,比如,不使用驗證,或者,使用圖片驗證服務(wù)等。 |
onFail | function(code:int, msg:string, retry:function) | F | 用戶驗證失敗處理器, code: 錯誤碼,msg: 錯誤信息,retry: 重試驗證邏輯。默認實現(xiàn)為重新驗證一次。 |
beforeStart | function(next:function) | F | 進入驗證邏輯前的勾子。next: 繼續(xù)執(zhí)行后續(xù)邏輯 |
expired | int | F | 請求過期時限。單位秒,默認 30 |
jsonpField | string | F | jsonp 處理器名。默認為 ypjsonp |
rsaPublicKey | string | F | 加密公鑰。如非異常情況則無需設(shè)置 |
hosts | string | F | 驗證服務(wù)器地址。如非異常情況則無需設(shè)置 |
winWidth | number/string | F | 窗口寬度。寬度最小 230px: 兩種方式:1. 純數(shù)字格式,單位 px. 默認 500; 2. 百分比格式, 比如 80%,表示相對當前瀏覽器可視區(qū)域?qū)挾鹊陌俜直取2恍∮?230px |
lang | string | F | 支持語言,默認簡體中文。zh-cn(簡體中文)、en(英文) |
langPack | object | F | 外部導入所需設(shè)置語言文案。需按指定格式設(shè)置對象導入,當外部導入語言包時,lang 設(shè)置會自動失效 |
winWidth 窗口寬度配置
// 設(shè)置窗口寬度為固定值(px) new YpRiddler({ ... winWidth: '500' ... }) // 設(shè)置窗口寬度為屏幕寬度的百分比(窗口寬度winWidth = 屏幕寬度 * %) new YpRiddler({ ... winWidth: '30%' ... })
lang 配置(可選)
系統(tǒng)默認支持中文,如需要替換其他語言請進行如下配置。目前支持的語言有:簡體中文、英文。
如果需要設(shè)置文案的語言,可通過外部文件,按指定格式設(shè)置文案內(nèi)容,然后在 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="http://www.ssjdyy2.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, // 你可以通過該參數(shù)自定義語言包, 其優(yōu)先級高于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) { // 服務(wù)不可用時,開發(fā)者可采取替代方案,詳見 “get 接口響應碼釋義” console.error('驗證服務(wù)暫不可用'); } else if (param.code == 429) { console.warn('請求過于頻繁,請稍后再試'); } else if (param.code == 403) { console.warn('請求受限,請稍后再試'); } else if (param.code == 400) { console.warn('非法請求,請檢查參數(shù)'); } // 異常回調(diào) console.error('驗證服務(wù)異常') }, onSuccess: function (validInfo, close, useDefaultSuccess) { // 成功回調(diào) alert('驗證通過! token=' + validInfo.token + ', authenticate=' + validInfo.authenticate) // 驗證成功默認樣式 useDefaultSuccess(true) close() }, onFail: function (code, msg, retry) { // 失敗回調(diào) 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="http://www.ssjdyy2.com/static/official/js/libs/riddler-sdk-0.2.2.js?t=20210720"></script>
使用方法如下:
import React from 'react' // 如下配置僅作為示例,具體可參考'配置驗證對象'小節(jié) 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() // 在需要展示行為驗證的時候,調(diào)用該方法 } render() { <!-- id名稱、樣式均可根據(jù)需要進行設(shè)置 --> 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="http://www.ssjdyy2.com/static/official/js/libs/riddler-sdk-0.2.2.js?t=20210720"></script>
使用方法如下:
<template> <!-- id名稱、樣式均可根據(jù)需要進行設(shè)置 --> <div id="cbox" style="width: 300px"></div> </template> <script> export default { name: 'App', methods: { // 如下配置僅作為示例,具體可參考'配置驗證對象'小節(jié) 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() // 在需要展示行為驗證的時候,調(diào)用該方法 } } </script>
接入成功樣例
前端接入完成后,通過谷歌瀏覽器的 Network 查看請求記錄,verify 請求會返回兩個參數(shù):authenticate和token。
后端接入
接口名稱
二次驗證接口
接口地址
https://captcha.yunpian.com/v1/api/authenticate
請求
- 請求方式:POST
- 請求類型:application/x-www-form-urlencoded
請求參數(shù)
參數(shù) | 類型 | 必填 | 備注 |
captchaId | string | Y | 驗證應用 id,對應用戶后臺分配的 APPID |
token | string | Y | 前端從 verfiy 接口獲取的 token,token 作為一次驗證的標志。 |
authenticate | string | Y | 前端從 verfiy 接口驗證通過后,返回的參數(shù) |
secretId | string | Y | 驗證應用密鑰 id |
version | string | Y | 版本,固定值 1.0 |
user | string | F | 可選值,接入方用戶標志,如擔心信息泄露,可采用摘要方式給出。 |
timestamp | string | Y | 當前時間戳的毫秒值,如 1541064141441 |
nonce | string | Y | 隨機正整數(shù), 在 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}";//用戶驗證通過后,返回的參數(shù) $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)); //隨機正整數(shù), 在 1-99999 之間 ksort($params); // 參數(shù)排序 $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); /* 設(shè)置返回結(jié)果為流 */ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); /* 設(shè)置超時時間*/ curl_setopt($ch, CURLOPT_TIMEOUT, 10); /* 設(shè)置通信方式 */ 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、簽名計算方法
- 第一步:對所有請求參數(shù)(不包括 signature 參數(shù)),按照參數(shù)名 ASCII 碼表升序順序排序。如:foo=1, bar=2, foo_bar=3, baz=4 排序后的順序是 bar=2, baz=4, foo=1, foo_bar=3 。
- 第二步:將排序好的參數(shù)名和參數(shù)值構(gòu)造成字符串,格式為:key1+value1+key2+value2… 。根據(jù)上面的示例得到的構(gòu)造結(jié)果為:bar2baz4foo1foo_bar3 。
- 第三步:選擇與 secretId 配對的 secretKey ,加到上一步構(gòu)造好的參數(shù)字符串之后,如 secretKey=e3da918313c14ea8b25db31f01263f80?,則最后的參數(shù)字符串為 ?bar2barz4foo1foo_bar3e3da918313c14ea8b25db31f01263f80。
- 第四步:把 3 步驟拼裝好的字符串采用 utf-8 編碼,使用 MD5 算法對字符串進行摘要,計算得到 signature 參數(shù)值,將其加入到接口請求參數(shù)中即可。MD5 是 128 位長度的摘要算法,用 16 進制表示,一個十六進制的字符能表示 4 個位,所以簽名后的字符串長度固定為 32 位十六進制字符。上述簽名的結(jié)果為:59db908f26fb997c30b32ddb911485c2。
/** * 生成簽名信息 * @param secretKey 應用私鑰 * @param params 接口請求參數(shù)名和參數(shù)值map,不包括signature參數(shù)名 * @return */ public static String genSignature(String secretKey, Map<String, String> params){ // 1. 參數(shù)名按照ASCII碼表升序排序 String[] keys = params.keySet().toArray(new String[0]); Arrays.sort(keys); // 2. 按照排序拼接參數(shù)名與參數(shù)值 StringBuilder sb = new StringBuilder(); for (String key : keys) { sb.append(key).append(params.get(key)); } // 3. 將secretKey拼接到最后 sb.append(secretKey); // 4. MD5是128位長度的摘要算法,轉(zhuǎn)換為十六進制之后長度為32字符 return DigestUtils.md5Hex(sb.toString().getBytes("UTF-8")); }
2、響應碼釋義
前端相關(guān)響應碼
verify 接口響應碼釋義
響應碼 | 錯誤信息 | 具體描述 |
0 | ok | 驗證通過 |
1 | bad request | 驗證請求數(shù)據(jù)缺失或格式有誤 |
2 | verify fail | 驗證不通過 |
400 | param_invalid | 請求參數(shù)錯誤,檢查 i k 參數(shù) |
400 | captcha_id_invalid | APPID 不存在 |
429 | too many requests | 請求過于頻繁,請稍后再試 |
500 | server_error | 服務(wù)異常 |
get 接口響應碼釋義
響應碼 | 錯誤信息 | 具體描述 |
0 | ok | 獲取驗證圖片成功 |
400 | param_invalid | 請求參數(shù)錯誤,檢查 i k 參數(shù) |
403 | forbidden request | 異常請求,已攔截,需要一段時間才允許恢復訪問,可能是訪問過于頻繁導致 |
400 | captcha_id_invalid | APPID 不存在 |
429 | too many requests | 請求過于頻繁,請稍后再試 |
500 | server_error | 服務(wù)異常 |
531 | server_reject | 拒絕提供服務(wù),例如驗證量超過套餐額度后,可能會拒絕服務(wù),同其他5xx錯誤碼一樣,應考慮回退方案 |
后端相關(guān)響應碼
響應參數(shù)
參數(shù) | 類型 | 必填 | 備注 |
code | int | Y | 成功為 0,非 0 為異常信息,詳見下方“二次驗證接口響應碼釋義” |
msg | string | Y | 錯誤描述信息 |
二次驗證接口響應碼釋義
響應碼 | 錯誤信息 | 具體描述 |
0 | ok | 二次驗證通過 |
400 | validate_fail | 二次驗證不通過 |
400 | signature_invalid | 簽名校驗失敗 |
400 | param_invalid | 請求參數(shù)錯誤,檢查 i k 參數(shù) |
400 | captcha_id_invalid | APPID 不存在 |
400 | re_authenticate | 重復的authenticate和token |
429 | too many requests | 請求過于頻繁,請稍后再試 |
500 | server_error | 服務(wù)異常 |