集成阿里雲滑動驗證(python)

阿里雲的服務 數據風控下面有項滑動驗證, 適合有人機驗證場景的應用接入.javascript

所謂滑動驗證, 是在須要進行人機識別時, 前端出現驗證滑塊, 經過拖動滑塊到末尾, 實現驗證. 若是阿里雲認爲這次驗證風險稍高, 可能出現點擊漢字的認證等, 若是風險過高, 驗證會直接拒絕.css

集成過程基本是按照 阿里雲官方文檔 來進行的.html

開通滑動驗證服務以後, 阿里雲控制檯會給出先後端的示例代碼, 其中appkey是根據用戶不一樣分配的不通的key.前端

前端代碼以下所示:java

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>示例-WEB版</title>

  <!-- 此段必需要引入 t爲小時級別的時間戳 -->
  <link type="text/css" href="http://g.alicdn.com/sd/ncpc/nc.css?t=1502182314627" rel="stylesheet"/>
  <script type="text/javascript" src="http://g.alicdn.com/sd/ncpc/nc.js?t=1502182314627"></script>
  <!-- 引入結束 -->  

</head>
<body>

<!-- 此段必需要引入 -->
<div id="_umfp" style="display:inline;width:1px;height:1px;overflow:hidden"></div>
<!-- 引入結束 -->

<!-- 表單示例,請替換成您的業務表單 -->
<div class="container">
  <form action="您提交的服務端地址" method="post">
    <div class="ln">
      <span class="h">用戶名:</span>
      <input type="text" name="username">
    </div>

    <div class="ln">
      <span class="h">密碼:</span>
      <input type="password" name="password">
    </div>

    <div class="ln">
      <div id="dom_id"></div>
    </div>

    <input type='hidden' id='csessionid' name='csessionid'/>
    <input type='hidden' id='sig' name='sig'/>
    <input type='hidden' id='token' name='token'/>
    <input type='hidden' id='scene' name='scene'/>
    <div class="ln">
      <input type="submit" value="提交">
    </div>
  </form>
</div>
<!-- 表單示例結束 -->

<!-- 此段必需要引入 -->
<script>
  var nc = new noCaptcha();
  var nc_appkey = '你的appkey';  // 應用標識,不可更改
  var nc_scene = 'other';  //場景,不可更改
  var nc_token = [nc_appkey, (new Date()).getTime(), Math.random()].join(':');
  var nc_option = {
    renderTo: '#dom_id',//渲染到該DOM ID指定的Div位置
    appkey: nc_appkey,
    scene: nc_scene,
    token: nc_token,
    trans: '{"name1":"code0"}',//測試用,特殊nc_appkey時才生效,正式上線時請務必要刪除;code0:經過;code100:點擊驗證碼;code200:圖形驗證碼;code300:惡意請求攔截處理
    callback: function (data) {// 校驗成功回調
      console.log(data.csessionid);
      console.log(data.sig);
      console.log(nc_token);

      document.getElementById('csessionid').value = data.csessionid;
      document.getElementById('sig').value = data.sig;
      document.getElementById('token').value = nc_token;
      document.getElementById('scene').value = nc_scene;
    }
  };
  nc.init(nc_option);
</script>
<!-- 引入結束 -->

</body>

<!-- 樣式示例,請替換成本身的樣式 -->
<style>
    body {
      background: #f5f5f5;
      font-size: 14px;
      line-height: 20px;
      margin: 0;
      padding: 0;
    }
    .container {
      background: #fff;
      padding: 20px;
      margin: 20px;
      width: 400px;
    }
    .ln {
      padding: 5px 0;
    }
    .ln .h {
      display: inline-block;
      width: 4em;
    }
    .ln input {
      border: solid 1px #999;
      padding: 5px 8px;
    }
  </style>
<!-- 樣式示例結束 -->

</html>

其中標記不可更改的地方就不要更改. 能夠看到, 在驗證經過以後, console會打印出session, sig, token等幾個驗證返回變量, 連同scene一塊兒, 經過表單返回業務後臺, 由後臺調用阿里雲滑動驗證服務進行校驗.python

python後臺方面, 首先須要算法

pip install aliyun-python-sdk-jaq

安裝阿里雲滑動驗證的python sdk
python的後臺代碼大體以下:apache

# coding: utf-8
import requests
from aliyunsdkcore import client
from aliyunsdkjaq.request.v20161123 import AfsCheckRequest
from aliyunsdkcore.profile import region_provider
from app.libs.configure import config

region_provider.modify_point('Jaq', 'cn-hangzhou', 'jaq.aliyuncs.com')

clt = client.AcsClient(config.ALIYUN_OSS_ACCESS_KEY, config.ALIYUN_OSS_ACCESS_SECRET, 'cn-hangzhou')


def check_aliyun_captcha(session, sig, token, scene):
    request = AfsCheckRequest.AfsCheckRequest()
    # 必填參數:請求來源: 1:Android端; 2:iOS端; 3:PC端及其餘
    request.set_Platform(3)
    request.set_Session(session)
    request.set_Sig(sig)
    request.set_Token(token)
    request.set_Scene(scene)
    result = clt.do_action_with_exception(request)
    print result

這只是個簡單示例, print出了返回結果. 這個返回結果是json序列化的string, 須要將其反序列化進行結果斷定. 若是Data元素是true的話, 證實校驗經過, 能夠進行下面的業務邏輯.
一樣的, 這裏還有一點沒有處理, 就是do_action_with_exception的拋出ServerException的問題. 應當catch處理.json

若是驗證經過的話, 阿里雲返回:後端

{"Data":true,"ErrorMsg":"success.","ErrorCode":0}

但只能驗證一次, 第二次一樣參數執行的話, 會返回:

{"Data":false,"ErrorMsg":"invalid sig parameter.","ErrorCode":400}

是爲了防止暴力嘗試等.

同時必須說明, 阿里雲的這項服務我認爲並不穩定, 集成中間出現了一直invalid sig的報錯. 後來經與技術支持溝通才ok. 而且這塊的文檔友好程度等還比較欠缺.

另: 我還按照阿里雲api文檔(滑動驗證api, 公共參數, 簽名機制)的說明寫了段代碼, 不使用aliyun-python-sdk, 手動拼接請求.一樣發現了不少問題.
好比文檔中有一段

clipboard.png
這個簽名值應當不是隨便寫的. 使用文檔中要求的HMAC-SHA1簽名算法, 不管是用python像以下這樣書寫:

from hashlib import sha1
import hmac
from base64 import b64encode

hashed = hmac.new('testsecret&', string_to_sign, sha1)
signature = b64encode(hashed.digest())
print signature

仍是像以下用Java算簽名:

import javax.crypto.Mac;  
import javax.crypto.SecretKey;  
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Base64;  
  
public class HMACSHA1 {  
  
    private static final String MAC_NAME = "HmacSHA1";    
    private static final String ENCODING = "UTF-8";    

    public static byte[] HmacSHA1Encrypt(String encryptText, String encryptKey) throws Exception {           
        byte[] data=encryptKey.getBytes(ENCODING);  
        SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);   
        Mac mac = Mac.getInstance(MAC_NAME);   
        mac.init(secretKey);    
        byte[] text = encryptText.getBytes(ENCODING);    
        return mac.doFinal(text);    
    }
    
    public static void main(String[] args) throws Exception{
        byte[] bytes = HMACSHA1.HmacSHA1Encrypt("string_to_sign", "testsecret&");
        System.out.println(Base64.encodeBase64String(bytes));
    }
}

string_to_sign用文檔中給出的替換, 個人兩段程序算出的是一致的, 但都跟文檔中給出的不一致. 不清楚爲什麼基礎算法會不一致, 難道是文檔不夠新.

若是我用python以下根據api文檔實現本身的後臺請求方法的話:

import random
from urllib import quote
from hashlib import sha1
import hmac
import requests
from base64 import b64encode
import datetime

def utcnow_isostr():
    dt = datetime.datetime.utcnow()
    return datetime.datetime.strftime(dt, '%Y-%m-%dT%H:%M:%SZ')

def quote_ali(element):
    return quote(str(element)).replace('+', '20%').replace('*', '2A%').replace('%7E', '~')

def build_afs_check_request(session, sig, token, scene):
    m = dict()
    m['Action'] = 'AfsCheck'
    m['Format'] = 'JSON'
    m['Version'] = '2016-11-23'
    m['AccessKeyId'] = config.ALIYUN_OSS_ACCESS_KEY
    m['SignatureMethod'] = 'HMAC-SHA1'
    m['Timestamp'] = utcnow_isostr()
    m['SignatureVersion'] = '1.0'
    m['SignatureNonce'] = str(int(random.random()*1000000))
    m['token'] = token
    m['sig'] = sig
    m['session'] = session
    m['scene'] = scene
    m['platform'] = 3
    ks = m.keys()
    ks.sort()
    query_list = list()
    for k in ks:
        query_list.append(k + '=' + quote_ali(m[k]))
    string_to_sign = 'GET&%2F&' + '&'.join(query_list)
    print string_to_sign
    base_query_string = '&'.join(query_list)
    hashed = hmac.new(config.ALIYUN_OSS_ACCESS_SECRET + '&', string_to_sign, sha1)
    signature = b64encode(hashed.digest())
    print signature
    url = 'http://jaq.aliyuncs.com/?{}&Signature={}'.format(base_query_string, quote_ali(signature))
    print url
    return url

def custom_check_aliyun_captcha(session, sig, token, scene):
    url = build_afs_check_request(session, sig, token, scene)
    resp = requests.get(url)
    print resp.content

這麼去請求的話會報Signature不一致, 果不出所料.

基於以上詭異的表現, 我以爲阿里雲滑動驗證做爲外部服務並不友好或成熟. 更傾向於用本身作的一些驗證碼服務, 雖然會簡單些, 更容易被攻破, 至少穩定, 提供的qps更高.

相關文章
相關標籤/搜索