在調(diào)用阿里云 API 的時(shí)候,最讓人頭疼的就是 API 的簽名(Signature)機(jī)制略吨,阿里云在通用文檔中也有專項(xiàng)說明,但是僅僅有基于 Java 的實(shí)現(xiàn)代碼示例。所以這里基于 Python 來分析下发皿。
基本步驟
- 構(gòu)造規(guī)范化的請求字符串 (Canonicalized Query String)
- 構(gòu)造被簽名字符串 StringToSign
- 計(jì)算 HMAC 值
- 計(jì)算簽名值
- 添加簽名
理論部分的詳細(xì)內(nèi)容參考阿里云官方幫助文檔吧
具體 Python 實(shí)現(xiàn)
API 請求原理
簡單來說調(diào)用阿里云 API 就是一個(gè) http 請求(大多數(shù)為 GET, 這里也是基于 GET 請求),只是后面要跟一堆參數(shù)拂蝎,例如一個(gè)查看快照的請求為:
http://ecs.aliyuncs.com/?SignatureVersion=1.0&Format=JSON&Timestamp=2017-08-07T05%3A50%3A57Z&RegionId=cn-hongkong&AccessKeyId=xxxxxxxxx&SignatureMethod=HMAC-SHA1&Version=2014-05-26&Signature=%2FeGgFfxxxxxtZ2w1FLt8%3D&Action=DescribeSnapshots&SignatureNonce=b5046ef2-7b2b-11e7-a3c5-00163e001831&ZoneId=cn-hongkong-b
請求中所需要的公共參數(shù)(就是調(diào)用 API 都需要用到的參數(shù))為:
SignatureVersion # 簽名算法版本穴墅,目前為 1.0
Format # 返回消息的格式化方式,JSON or XML 默認(rèn)值為 XML
Timestamp # 請求的時(shí)間戳温自,UTC時(shí)間玄货,例如: 2013-01-10T12:00:00Z
AccessKeyId # 賬號密鑰 ID
SignatureMethod # 簽名方式,目前為 HMAC-SHA1
Version # 版本號悼泌,為日期形式松捉,例如: 2014-05-26 每個(gè)產(chǎn)品不同
Signature # 最難搞定的簽名
SignatureNonce # 唯一隨機(jī)數(shù),防止網(wǎng)絡(luò)攻擊券躁。不同請求間使用不同的隨機(jī)數(shù)惩坑。
除了 Signature
之外,其它的參數(shù)都比較容易獲得也拜,有些甚至是固定的值以舒,具體可以參考阿里云文檔
除了公共參數(shù)之外,還需要具體接口(Action)的請求參數(shù)慢哈,每個(gè) Action
接口的參數(shù)可以參考對應(yīng)產(chǎn)品的接口文檔蔓钟,例如 DescribeLoadBalancers
而 Signature
是基于公共參數(shù)和接口參數(shù)的,所以比較復(fù)雜卵贱。
Signature 具體代碼實(shí)現(xiàn)
構(gòu)造規(guī)范化的請求字符串 (Canonicalized Query String)
- 構(gòu)造 dict
Python 中體現(xiàn)參數(shù)一一對應(yīng)的就是 dict, 創(chuàng)建一個(gè) dict, 把請求參數(shù)些進(jìn)去滥沫,這里簡化參數(shù)只有這些:
>>> D = {
'Format':'JSON',
'Version':'2014-05-26',
'SignatureMethod':'HMAC-SHA1'
}
- 排序
由于簽名要求唯一性,包括順序键俱,所以需要按照參數(shù)名稱排序
>>> sortedD = sorted(D.items(), key=lambda x: x[0])
>>> sortedD
[('Format', 'JSON'),
('SignatureMethod', 'HMAC-SHA1'),
('Version', '2014-05-26')]
# 先通過 D.items() 轉(zhuǎn)化為 List, 然后利用 sorted 方式按照 key 排序
- URL 編碼
由于在標(biāo)準(zhǔn)請求字符串中需要使用 UTF-8 字符集兰绣,對請求參數(shù)名稱和值中有些不符合規(guī)范的字符要進(jìn)行 url 編碼,具體規(guī)則為:
字符 AZ编振、az缀辩、0~9 以及字符“-”、“_”、“.”臀玄、“~”不編碼瓢阴;
其它字符編碼成 %XY 的格式,其中 XY 是字符對應(yīng) ASCII 碼的 16 進(jìn)制表示健无。比如英文的雙引號(”)對應(yīng)的編碼為 %22荣恐;
對于擴(kuò)展的 UTF-8 字符,編碼成 %XY%ZA… 的格式累贤;
英文空格( )要編碼成 %20叠穆,而不是加號(+)。
注意:一般支持URL編碼的庫(比如 Java 中的 java.net.URLEncoder)都是按照 “application/x-www-form-urlencoded”的 MIME 類型的規(guī)則進(jìn)行編碼的畦浓。實(shí)現(xiàn)時(shí)可以直接使用這類方式進(jìn)行編碼痹束,把編碼后的字符串中加號(+)替換成 %20、星號(*)替換成 %2A讶请、%7E 替換回波浪號(~)祷嘶,即可得到上述規(guī)則描述的編碼字符串。
這里使用 python 中的 urllib
庫來進(jìn)行編碼:
>>> def percentEncode(str):
res = urllib.quote(str.decode(sys.stdin.encoding).encode('utf8'), '')
res = res.replace('+', '%20')
res = res.replace('*', '%2A')
res = res.replace('%7E', '~')
return res
# 這里構(gòu)造一個(gè)編碼函數(shù)夺溢,對一個(gè)字符串進(jìn)行編碼论巍,返回編碼后的字符串
- 生成標(biāo)準(zhǔn)化請求字符串
>>> canstring = ''
>>> for k,v in sortedD:
canstring += '&' + percentEncode(k) + '=' + percentEncode(v)
>>> canstring
'&Format=JSON&SignatureMethod=HMAC-SHA1&Version=2014-05-26'
構(gòu)造被簽名字符串 StringToSign
規(guī)則為:
StringToSign=
HTTPMethod + “&” +
percentEncode(“/”) + ”&” +
percentEncode(CanonicalizedQueryString)
所以在這個(gè)實(shí)例中
>>> stringToSign = 'GET&%2F&' + percentEncode(canstring[1:])
>>> stringToSign
'GET&%2F&Format%3DJSON%26SignatureMethod%3DHMAC-SHA1%26Version%3D2014-05-26'
# >>> percentEncode(“/”)
# %2F
計(jì)算 HMAC 值
>>> access_key_secret = 'access_key_secret'
>>> h = hmac.new(access_key_secret + "&", stringToSign, sha1)
>>> h
<hmac.HMAC instance at 0x35ed440>
# access_key_secret 是通過阿里云賬號中的 AK 中獲取的,和 access_key_id 對應(yīng)风响,測試的時(shí)候使用的是 'access_key_secret'
計(jì)算簽名值
>>> signature = base64.encodestring(h.digest()).strip()
>>> signature
'sq8LVH+ZItZiVQ0/rVnHV1kP/BE='
到此生成了 signature
簽名
添加簽名
>>> D['Signature'] = signature
>>> D
{'Format': 'JSON',
'Signature': 'sq8LVH+ZItZiVQ0/rVnHV1kP/BE=',
'SignatureMethod': 'HMAC-SHA1',
'Version': '2014-05-26'}
所以在這個(gè)實(shí)例中嘉汰,最終請求的 url 為
>>> url = 'http://ecs.aliyuncs.com/?' + urllib.urlencode(D)
>>> url
'http://ecs.aliyuncs.com/?SignatureMethod=HMAC-SHA1&Version=2014-05-26&Signature=sq8LVH%2BZItZiVQ0%2FrVnHV1kP%2FBE%3D&Format=JSON'
拿到瀏覽器直接訪問即可,得到結(jié)果為:
{"Message":"The input parameter \"Action\" that is mandatory for processing this request is not supplied.","RequestId":"129880D4-710D-4D2C-9F8B-12777FA1D3C6","HostId":"ecs.aliyuncs.com","Code":"MissingParameter"}
由于是測試環(huán)境状勤,就給了三個(gè)參數(shù)鞋怀,所以還少很多參數(shù),正常來說把這些參數(shù)都加上持搜,然后生成 signature
密似,組成 url 后直接訪問就可以得到結(jié)果。