身份验证
注意
身份验证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时区 |
host | http请求默认值: 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:
名称 | 示例值 | 备注 |
---|---|---|
a1 | webull | 值没有经过urlencode |
a2 | 123 | 值没有经过urlencode |
a3 | xxx | 值没有经过urlencode |
q1 | yyy | 值没有经过urlencode |
Headers:
名称 | 示例值 | 备注 |
---|---|---|
x-app-key | 776da210ab4a452795d74e726ebd74b6 | |
x-timestamp | 2022-01-04T03:55:31Z | |
x-signature-version | 1.0 | |
x-signature-algorithm | HMAC-SHA1 | |
x-signature-nonce | 48ef5afed43d4d91ae514aaeafbc29ba | |
host | api.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
}