跳至主要内容

身份驗證

注意

身份驗證Webull SDK中已經做好封裝處理,無需額外處理,這裡只是介紹簽名生成流程。

目錄

1> 簽名生成說明
2> 簽名內容構成
3> 簽名構造示例
4> URL編碼注意事項
5> 其他說明

簽名生成說明

簽名值sig是將請求源串以及密鑰根據一定簽名方法生成的簽名值, 用來提高傳輸過程參數的防篡改性.
簽名值的生成共有3個步驟: 構造源串, 構造密鑰, 生成簽名值.

簽名內容構成

簽名源串主要由4個部分內容組成:

1> Http Request Uri 
2> Http Url 中的 QueryParams
3> Http 請求 Body
4> Http 請求 Headers
// 注意: 當前不支持 http post 方式在 body 中傳遞 k=v 格式參數, 目前只支持 application/json

簽名相關 header 約定如下::

名稱說明
x-app-key訪問密鑰ID
x-signature簽名值
x-signature-algorithm簽名算法, 默認HMAC-SHA1
x-signature-version簽名算法版本, 默認1.0
x-signature-nonce簽名唯一隨機數
x-timestamp請求的時間戳, ISO8601的時間格式, UTC時區
hosthttp請求默認值: host:port, 示例: api.webull.com:8080

簽名規則如下:

1> 參與簽名計算的內容
1.1> http request uri
1.2> http request query params (注意: param 為沒有 urlencode 的原數據)
1.3> http request body
1.4> http request headers, 包含: x-app-key、x-signature-algorithm、x-signature-version、x-signature-nonce、x-timestamp、host
2> 簽名內容組織方式:
2.1> query params 和 headers 按照 k,v 組織成 map 結構, 然後按照 k 字符串值從小到大進行排序, 然後按照 k1=v1&k2=v2 方式進行連接, 最終得到 s1
2.2> 對 http request body 進行 md5 處理, 方式: upcase(md5(body)), 得到: s2 (注意: body 為空不參與到簽名中)
2.3> 拼接: uri + "&" + s1 + "&" + s2, 從而得到: s3
注意: body 為空時, s3 = uri + '&' + s1
3> 生成簽名: signature = base64(HMAC-SHA1(app_secret + "&", encoded_sign_string))

簽名構造示例

請求內容

uri: /trade/place_order

QueryParams:

名稱示例值備註
a1webull值沒有經過urlencode
a2123值沒有經過urlencode
a3xxx值沒有經過urlencode
q1yyy值沒有經過urlencode

Headers:

名稱示例值備註
x-app-key776da210ab4a452795d74e726ebd74b6
x-timestamp2022-01-04T03:55:31Z
x-signature-version1.0
x-signature-algorithmHMAC-SHA1
x-signature-nonce48ef5afed43d4d91ae514aaeafbc29ba
hostapi.webull.com

Body: {"k1":123,"k2":"this is the api request body","k3":true,"k4":{"foo":[1,2]}}

Secret: 0f50a2e853334a9aae1a783bee120c1f

Step1: 構造源串

將 按照 key 進行從小到大字典排序, 得到如下順序數據:
Key: a1 , Value: webull
Key: a2 , Value: 123
Key: a3 , Value: xxx
Key: host , Value: api.webull.com
Key: q1 , Value: yyy
Key: x-app-key , Value: 776da210ab4a452795d74e726ebd74b6
Key: x-signature-algorithm , Value: HMAC-SHA1
Key: x-signature-nonce , Value: 48ef5afed43d4d91ae514aaeafbc29ba
Key: x-signature-version , Value: 1.0
Key: x-timestamp , Value: 2022-01-04T03:55:31Z

OriginBody: {"k1":123,"k2":"this is the api request body","k3":true,"k4":{"foo":[1,2]}}
Body MD5后: ToUpper(MD5(body)) = E296C96787E1A309691CEF3692F5EEDD
注意: 有一些 json 工俱生成的 json 串在 key 和 value 之間包含空格, 會導致驗證不通過

拼接後的源串如下:
/trade/place_order&a1=webull&a2=123&a3=xxx&host=api.webull.com&q1=yyy&x-app-key=776da210ab4a452795d74e726ebd74b6&x-signature-algorithm=HMAC-SHA1&x-signature-nonce=48ef5afed43d4d91ae514aaeafbc29ba&x-signature-version=1.0&x-timestamp=2022-01-04T03:55:31Z&E296C96787E1A309691CEF3692F5EEDD

encodeURIComponent後得到encoded_sign_string:
%2Ftrade%2Fplace_order%26a1%3Dwebull%26a2%3D123%26a3%3Dxxx%26host%3Dapi.webull.com%26q1%3Dyyy%26x-app-key%3D776da210ab4a452795d74e726ebd74b6%26x-signature-algorithm%3DHMAC-SHA1%26x-signature-nonce%3D48ef5afed43d4d91ae514aaeafbc29ba%26x-signature-version%3D1.0%26x-timestamp%3D2022-01-04T03%3A55%3A31Z%26E296C96787E1A309691CEF3692F5EEDD

Step2: 添加密鑰

得到密鑰的方式: 在應用的app_secret末尾加上一個字節的"&", 即app_secret&, 例如:

0f50a2e853334a9aae1a783bee120c1f&

Step3: 生成簽名值

生成方式: 
signature = base64(HMAC-SHA1(app_secret + "&", encoded_sign_string))

1> 使用HMAC-SHA1加密算法, 使用 Step2 中得到的密鑰對 Step1 中得到的源串加密
2> 然後將加密後的字符串經過Base64編碼
3> 得到的簽名值結果如下: kvlS6opdZDhEBo5jq40nHYXaLvM=

URL編碼注意事項

簽名驗證時, 要求對字符串中除了“-”、“_”、“.”之外的所有非字母數字字符都替換成百分號(%)後跟兩位十六進制數,十六進制數中字母必須為大寫.

其他說明

1> 一些特殊情況, 比如 Query 參數中存在多個 key的情況, value 值需要按照 value 進行升序排序, 然後用 '&' 連接. 舉例:
url?k1=v1&k1=v2&k1=v3
連接處理後: k1 = v1&v2&v3 , 如不這樣處理, 會導致簽名不通過
2> Golang json.Marshal 默認 escapeHtml 為true,會轉義 <、>、&, 這種情況需要對對這個轉義後的內容進行處理, 替換為原來的字符. 一般處理如下:
func trans(data []byte) []byte {
data = bytes.Replace(data, []byte("\\u0026"), []byte("&"), -1)
data = bytes.Replace(data, []byte("\\u003c"), []byte("<"), -1)
data = bytes.Replace(data, []byte("\\u003e"), []byte(">"), -1)
return data
}