以騰訊上傳用戶在應用中的等級相關信息為例。接口詳情在此【"http://wiki.open.qq.com/wiki/v3/user/get_info"】
在set_achievement這個接口中,接口參數包括openid, openkey, appid, pf, sig, user_attr, format
此接口最后的請求示例為
http://openapi.tencentyun.com/v3/user/set_achievement?
openid=B624064BA065E01CB73F835017FE96FA&
openkey=5F154D7D2751AEDC8527269006F290F70297B7E54667536C&
appid=2&
pf=qzone&
format=json&
user_attr=%7B%22level%22%3A10%7D&
sig=9999b41ad0b688530bb1b21c5957391c
這里除了sig其他參數都只是經過了URL編碼而已,所以本文的重點也在如何生成sig。
在寫代碼的過程中我們也可以思考下為什么需要生成簽名~
Step 1. 構造源串
第1步:將請求的URI路徑進行URL編碼(URI不含host,URI示例:/v3/user/get_info)。請開發者關注:URL編碼注意事項,否則容易導致后面簽名不能通過驗證。
第2步:將除“sig”外的所有參數按key進行字典升序排列。 注:除非OpenAPI文檔中特別標注了某參數不參與簽名,否則除sig外的所有參數都要參與簽名。
第3步:將第2步中排序后的參數(key=value)用&拼接起來,並進行URL編碼。請開發者關注:URL編碼注意事項,否則容易導致后面簽名不能通過驗證。 第4步:將HTTP請求方式(GET或者POST)以及第1步和第3步中的字符串用&拼接起來。
Step 2. 構造密鑰
得到密鑰的方式:在應用的appkey末尾加上一個字節的“&”,即appkey&
Step 3. 生成簽名值
1. 使用HMAC-SHA1加密算法,使用Step2中得到的密鑰對Step1中得到的源串加密。 (注:一般程序語言中會內置HMAC-SHA1加密算法的函數,例如PHP5.1.2之后的版本可直接調用hash_hmac函數。)
2. 然后將加密后的字符串經過Base64編碼。 (注:一般程序語言中會內置Base64編碼函數,例如PHP中可直接調用 base64_encode() 函數。)
3. 得到的簽名值結果如下:
FdJkiDYwMj5Aj1UG2RUPc83iokk=
def getSig(cfg): strFilter = ".-_" codeUrlAddr = urllib2.quote(cfg['urlAddr'],strFilter) urlData2 = sorted(cfg['urlData'].iteritems(), key=lambda d:d[0]) codeStr0 = "" for (value01,value02) in urlData2: if codeStr0: codeStr0 += "&" + str(value01) + '=' + str(value02) else: codeStr0 += str(value01) + '=' + str(value02) codeStr1 = urllib2.quote(codeStr0) codeConn = cfg['urlMethod'] + '&' + codeUrlAddr + '&' + codeStr1 sig = hmac.new(cfg['appkey'] + '&', codeConn, hashlib.sha1).digest().encode('base64').rstrip() return sig def test(): urlData = { 'openid' : '12345', 'openkey' : '12345', 'pf' : 'wanba_ts', 'appid' : 12345, 'format' : 'json', 'user_attr' : '{"level":%d}' % 1234 } urlcfg = { 'urlAddr' : '/v3/user/set_achievement', 'urlMethod' : 'GET', 'appkey' : 'ABCDWFSFFG', 'urlData' : urlData } urlData['sig'] = getSig(urlcfg)
第一步:將請求的URI路徑進行URL編碼(URI不含host,URI示例:/v3/user/get_info)。
urlCfg中urlAddr是第一步中的請求的URI路徑。
URL編碼規則:
簽名驗證時,要求對字符串中除了“-”、“_”、“.”之外的所有非字母數字字符都替換成百分號(%)后跟兩位十六進制數。
十六進制數中字母必須為大寫。
strFilter = ".-_" #不需要替換的字符 codeUrlAddr = urllib2.quote(cfg['urlAddr'],strFilter)# url編碼
第二步:將除“sig”外的所有參數按key進行字典升序排列。
urlData中就是第二步要求的sig參數外的所有參數,采用sorted方式進行排序。
urlData2 = sorted(cfg['urlData'].iteritems(), key=lambda d:d[0])
第三步:將第2步中排序后的參數(key=value)用&拼接起來,並進行URL編碼。
codeStr0 = "" for (value01,value02) in urlData2: if codeStr0: codeStr0 += "&" + str(value01) + '=' + str(value02) else: codeStr0 += str(value01) + '=' + str(value02) codeStr1 = urllib2.quote(codeStr0)
當我看到urllib.urlencode可以用來直接生成&進行拼接的時候非常開心,但是后來發現它會對value進行url編碼。這與我們的要求是不符合的。所以采用了上面這種詭異的方式。
第四步:將HTTP請求方式(GET或者POST)以及第1步和第3步中的字符串用&拼接起來。
codeConn = cfg['urlMethod'] + '&' + codeUrlAddr + '&' + codeStr1
Step2和Step3
sig = hmac.new(cfg['appkey'] + '&', codeConn, hashlib.sha1).digest().encode('base64').rstrip()
這樣就得到了sig的值,文檔看起來有些復雜,但是一步步做下來其實難度也不大。
接下來就是按照文檔要求進行數據請求啦~
urlStr1 = urllib.urlencode(urlData) url = urlHost + qqUserInfos['getPlayzoneUserInfoUrl'] + '?' + urlStr1 request = urllib2.Request(url) result = json.loads(urllib2.urlopen(request).read())
在這個過程中不知道有沒有小伙伴好奇為什么我的urlData里面的user_attr要像下面這樣寫
'user_attr' : '{"level":%d}' % 1234
其實剛開始我是直接把{"level:1234}這個字典賦值給user_attr的。但是在操作的過程中發現由於python會把雙引號自動變成單引號,導致生成的簽名一直有問題。所以采用json格式來解決這個問題。
你以為本文這就完了?當!然!不!是!還有剛開始提出的問題沒回答捏~
sooooo...為什么要生成簽名?
The answer is "為了確定數據來源正確合法"
2.生成簽名后,將用作簽名的參數和簽名一起傳給騰訊服務器。
3.服務器將簽名與我們的appid對應的appkey進行解密。
4.將傳遞過去的參數排序,然后與生成的參數進行對比。若數據一致則能保證數據真實。