一、签名认证流程
1.1 准备
客户端在调用服务端接口API之前,需要拿到已经授权给自己的账户密钥对:
- APP Key
- APP Secret
1.2 客户端生成签名
客户端生成签名步骤:
1、从要发送的HTTP请求中提取关键数据,得到一个用来签名的字符串。
2、使用加密算法加上APP Secret对上一步的字符串进行加密处理,得到签名。
3、将签名等信息放入到Header中,得到最终要发送的HTTP请求。
1.3 客户端生成签名
服务端验证签名步骤:
1、从接收的HTTP请求中提取关键数据,得到一个用来签名的字符串。
2、从请求中读取到APP Key,进而能知道客户端的APP Secret。
3、使用加密算法加上APP Secret对关键数据签名串进行加密处理,得到签名。
4、从接收的请求中读取到签名,对比两个签名是否一致。
二、签名认证详细步骤
2.1 签名串构成
客户端从要发送的HTTP请求中提取出关键数据,组成一个待签名的字符串,由下面五个部分组成:
- http method
- url path
- headers
- content md5
- query parameters
以上五个部分之间使用“\n”间隔,如有部分的内容为空则不需要保留“\n”, 字符串内容大小写敏感。
2.1.1 http method
获取请求的http方法,全部大写,比如 “GET”、“POST” 等。
2.1.2 url path
请求url的path部分,不包括host和query参数部分,比如 “/path/data”。
2.1.3 headers
选取请求头:
哪些请求头信息参与到签名,由Header是 tw-signature-headers 的内容决定。参与到签名的header的key的集合,使用英文逗号分割放到 key为 tw-signature-headers 的 Header中,客户端与服务端根据这个值进行选取并拼接签名串。
例如:
tw-signature-headers: tw-appkey,tw-signature-method,tw-nonce,tw-timastamp
可以选取的header列表如下,一共四个:
- tw-appkey:分配给客户端的账户名
- tw-nonce:客户端生成的随机字符串,用于防止重放攻击
- tw-timestamp:时间戳,精确到毫秒,用于防止重放攻击
- tw-signature-method:签名方法,支持 HmacSHA256、HmacSHA1
拼接请求头:
- 将参与签名计算的header的key转为全小写
- 将这些header的value值内容去掉开头结尾的空白字符
- 将header的key按照字典序排序后,拼接为key:value形式
- 将上述所有key:value形式的字符串用“\n”连接
K1 + ":" + V1 + "\n" + K2+ ":" + V2 + "\n" + ... + Kn + ":" + Vn
注意:
- 如果一个header的value为空,则使用 key+":" 参与签名,即需要保留key和英文冒号部分。
- 如果设置了 tw-signature-method 参与签名,但是http实际请求没传值或者值错误,默认采用HmacSHA256参与签名。
- 如果 tw-signature-headers内容为空,则header部分不参与签名计算。
2.1.4 content md5
如果请求包含body内容 (可以为空),则要基于body内容计算出md5值,只有在请求存在body且为非 form形式时才计算md5值。
form即content-type为如下形式:
- multipart/form-data
- application/x-www-form-urlencoded
Go语言示例:
func Md5(str string) string {
hash := md5.New()
hash.Write([]byte(str))
sum := hash.Sum(nil)
sign := hex.EncodeToString(sum)
return sign
}
2.1.5 query parameters
包含请求query参数 和 form表单提交的所有参数。
- 将参数的key按照字典序排序后进行下面方式的拼接,格式 K1+"="+V1+"&"+K2+"="+V2+"&" + ... + Kn+"="+Vn
- query和form参数的值value为空时,只保留key参与签名,"="不参与签名。
- query和form都不存在时,整体不参与签名。
- query和form存在数组参数时,取第一个value参与签名计算。
- query和form包含相同key时,取query的内容参与签名计算。
传数组方式举例:
2.2 计算签名
客户端从要发送的HTTP请求中提取出关键数据拼接成待签名字符串后,要使用相应的加密算法,并使用 APP Secret参与其中。最后再对结果进行编码处理,形成最终的签名,目前API支持两种加密算法:
- HmacSHA256
- HmacSHA1
将待签名字符串(StringToSign)使用 UTF-8 编码后得到Byte数组,然后使用加密算法对 Byte数组进行签名,签名使用 APP Secret的值作为key,最后使用十六进制进行转码,形成最终签名。
Go语言示例:
func HmacSha256(key, str string) string {
hash := hmac.New(sha256.New, []byte(key))
hash.Write([]byte(str))
sum := hash.Sum(nil)
sign := hex.EncodeToString(sum)
return sign
}
2.3 传输签名
客户端需要将以下内容放入到http请求的header中,请求给到服务端网关,API网关会做签名比对。
tw-signature-headers: tw-appkey,tw-signature-method,tw-nonce,tw-timastamp
- tw-appkey: 分配给客户端的账户名。必填
- tw-signature:签名。必填
- tw-signature-method:签名算法,取值HmacSHA256或者HmacSHA1,默认值为HmacSHA256。选填
- tw-signature-headers: 所有参与签名的header头key的集合,使用逗号分隔。选填
- tw-nonce:随机字符串,用于防止防重放攻击。选填
- tw-timestamp:时间戳,精确到毫秒,用于防止重放攻击。选填
三、示例
3.1 GET 示例
curl --location --request GET --X GET 'http://localhost/hello/demo1?name=tom&detail=yes' \
--header 'tw-appkey: abcde' \
--header 'tw-signature-headers: tw-appkey,tw-signature-method'
步骤:
- 请求方法, GET
- 请求url的path, /hello/demo1
- 参照tw-signature-headers中的内容,获取2个header内容拼接字符串,每个header部分用"\n"回车符连接。签名方法没有传,所以默认用HmacSHA256。
tw-appkey:aaabbb
tw-signature-method:HmacSHA256
- 由于是GET请求,不处理body部分,content md5没有。
- 有query参数部分,name和detail按照字典序排序后拼接得到字符串,detail=yes&name=tom
- 把上面每个部分的内容用回车符拼接,得到待签名字符串
GET
/hello/demo1
tw-appkey:aaabbb
tw-signature-method:HmacSHA256
detail=yes&name=tom
- 对上面字符串,采用HmacSHA256签名,使用APP Secret值作为密钥,然后进行十六进制转码。
- 把签名放到 header头 tw-signature中一起发送。
3.2 POST表单示例
curl --location --request POST --X POST 'http://localhost/hello/demo2?name=tom&detail=yes' \
--header 'tw-nonce: asfaw345gee54feg' \
--header 'tw-timestamp: 1723081712335' \
--header 'tw-appkey: aaabbb' \
--header 'tw-signature-headers: tw-appkey,tw-signature-method,tw-nonce,tw-timestamp' \
--header 'tw-signature-method: HmacSHA1' \
--form 'username=john' \
--form 'password=admin'
步骤:
- 请求方法,POST
- 请求url的path, /hello/demo2
- 参照tw-signature-headers中的内容,获取4个header内容拼接字符串,每个header部分用"\n"回车符连接。
tw-appkey:aaabbb
tw-nonce:asfaw345gee54feg
tw-signature-method:HmacSHA1
tw-timestamp:1723081712335
- 虽然是POST请求,但是属于form表单,不处理body部分,content md5没有。
- 有query部分,也有form表单参数部分,所有字段按照字典序排序后拼接得到字符串,detail=yes&name=tom&password=admin&username=john
- 把上面每个部分的内容用回车符拼接,得到待签名字符串
POST
/hello/demo2
tw-appkey:aaabbb
tw-nonce:asfaw345gee54feg
tw-signature-method:HmacSHA1
tw-timestamp:1723081712335
detail=yes&name=tom&password=admin&username=john
- 签名方法传了HmacSHA1,对上面字符串,采用HmacSHA1签名,使用APP Secret值作为密钥,然后进行十六进制转码。
- 把签名放到 header头 tw-signature中一起发送。
3.3 POST json示例
curl --location --request POST --X POST 'http://localhost/hello/demo3' \
--header 'tw-nonce: asfaw345gee54feg' \
--header 'tw-timestamp: 1723081712335' \
--header 'tw-appkey: aaabbb' \
--header 'tw-signature-headers: tw-appkey,tw-signature-method,tw-nonce,tw-timestamp' \
--header 'tw-signature-method: HmacSHA256' \
--header 'Content-Type: application/json' \
--data '{
"name": "john"
}'
步骤:
- 请求方法,POST
- 请求url的path, /hello/demo3
- 参照tw-signature-headers中的内容,获取4个header内容拼接字符串,每个header部分用"\n"回车符连接。
POST
/hello/demo3
tw-appkey:aaabbb
tw-nonce:asfaw345gee54feg
tw-signature-method:HmacSHA256
tw-timestamp:1723081712335
- POST请求,且非form提交方式,查看到有body内容,对json内容进行md5,得到be6c45653b7a872980b27d9dd8d2e0ac
- 没有query参数,因此内容为空
- 把上面每个部分的内容用回车符拼接,得到待签名字符串
POST
/hello/demo3
tw-appkey:aaabbb
tw-nonce:asfaw345gee54feg
tw-signature-method:HmacSHA256
tw-timestamp:1723081712335
be6c45653b7a872980b27d9dd8d2e0ac
- 签名方法传了HmacSHA256,对上面字符串,采用HmacSHA256签名,使用APP Secret值作为密钥,然后进行十六进制转码。
- 把签名放到 header头 tw-signature中一起发送。