一、背景

最近負(fù)責(zé)的項(xiàng)目接口簽名規(guī)則做了調(diào)整,第一次接觸“2次認(rèn)證“的方式,正好有時(shí)間,記錄一下。

測(cè)試的服務(wù)A有一部分接口需要給第三方調(diào)用,這樣需要對(duì)第三方有個(gè)認(rèn)證,認(rèn)證是由一個(gè)公共服務(wù)(API鑒權(quán)服務(wù))來完成的。

基本框架:

 

調(diào)用時(shí)序圖:

 

第三方調(diào)用服務(wù)A的認(rèn)證流程:

1、先訪問API鑒權(quán)服務(wù)來獲取apiToken(即拿到訪問服務(wù)A的認(rèn)證)

2、再由獲取到的apiToken參與服務(wù)A的簽名認(rèn)證規(guī)則

這樣就相當(dāng)于在第三方和服務(wù)A直接增加了2次認(rèn)證,安全性有了更好的保障。

流程1(獲取apiToken)的簽名規(guī)則如下:

1、將所有請(qǐng)求參數(shù)的值放入List中,注意:計(jì)算sign時(shí)所有參數(shù)不應(yīng)進(jìn)行URLEncode;

2、將格式化后的參數(shù)以字典序升序排列,拼接在一起,注意字典序中大寫字母在前,空值(null)使用空字符串代替;

3、將B形成字符串獲取SHA1摘要,形成一個(gè)40位的十六進(jìn)制(字母大寫)字符串,即為本次請(qǐng)求signature(簽名)的值;   

流程2(獲取服務(wù)A的簽名)的規(guī)則如下:

復(fù)制代碼
1、參與簽名的參數(shù)為:apiToken+appKey+appSecret+timestamp+body(提取body中的所有參數(shù))

2、將格式化后的參數(shù)以字典序升序排列,拼接在一起

3、將第二步形成字符串獲取SHA1摘要

4、第三步獲得的字符串即為簽名參數(shù)
復(fù)制代碼

二、代碼實(shí)現(xiàn)

規(guī)則同時(shí)也是簽名的構(gòu)造方法,按照上面所列的步驟用Python來實(shí)現(xiàn)。

獲取apiToken:

請(qǐng)求參數(shù)有3個(gè):

appKey:應(yīng)用KEY(必填項(xiàng))

timestamp: 訪問時(shí)間戳(必填項(xiàng)),Unix時(shí)間戳;

signature:簽名(必填項(xiàng))

復(fù)制代碼
 1 """
 2 Created on 2019年04月03日
 3 @author: 
 4 """
 5 
 6 import time
 7 import hashlib
 8 import requests
 9 import operator
10 import json
11 
12 appKey = "n3nuk67byade3c3qgrccjhosvmqfzt7z5wavp3ig"
13 
14 appSecret = "b3a3056ef7ffb441332892ed76998b2e"
15 
16 time_stamp = str(int(time.time()))
17 
18 url = "http://10.10.10.100:8080/rest/v1/token/get"
19 
20 
21 class get_tokenclass():
22 
23     # 生成字符串
24     def str_create(self):
25         if operator.lt(appKey[0], appSecret[0]) == bool(1):           #py3中operator類和py2中cmp()函數(shù)的作用相似,通過比較2個(gè)值的大小,返回布爾類型
26             strnew = time_stamp + appKey + appSecret
27         else:
28             strnew = time_stamp + appSecret + appKey
29         print(strnew)
30         return strnew
31 
32 
33     # 生成signature
34     def signature_create(self):
35         str_switch = self.str_create()
36         signature = hashlib.sha1(str_switch.encode('utf-8')).hexdigest().upper().strip()
37         print(signature)
38         return signature
39 
40 
41     # 生成token
42     def token_creat(self):
43         signature = self.signature_create()
44         params = {"appKey":appKey, "timestamp":time_stamp, "signature":signature}
45         res = requests.get(url=url,params=params)
46         print(res.url)
47         print(json.loads(res.content.decode('utf-8')))
48         token = json.loads(res.content.decode('utf-8'))['result']['token']           #字節(jié)型的response轉(zhuǎn)換成字符串型,再轉(zhuǎn)換成字典型
49         print(token)
50         return token
51 
52 
53 if __name__ == '__main__':
54     tc = get_tokenclass()
55     # str_create()
56     # signature_create()
57     tc.token_creat()
58     # tc.str_create()
59     # tc.signature_create()
復(fù)制代碼

測(cè)試用例:

測(cè)試用例用unittest框架來組織

復(fù)制代碼
 1 """
 2 Created on 2019年04月03日
 3 @author: 
 4 """
 5 
 6 import requests
 7 import unittest
 8 import get_token
 9 from get_token import get_tokenclass
10 import json
11 import re
12 import hashlib
13 import random
14 
15 
16 class Test(unittest.TestCase):
17 
18     def setUp(self):
19         token_class = get_tokenclass()
20         self.apiToken = token_class.token_creat()
21         self.sign = token_class.signature_create()
22         self.timeSTAP = get_token.time_stamp
23         self.appKey = get_token.appKey
24         self.appSecret = get_token.appSecret
25         self.base_url = "http://10.10.10.100:8080"
26         self.headers = {"Content-type": "application/json", "Connection": "close"}
27         self.requestId = str(random.randint(0, 99999))       #每次請(qǐng)求(每個(gè)case)生成不同的隨機(jī)requestId
28 
29 
30     def tearDown(self):
31         pass
32 
33 
34     # 刪除酒店
35     def test_001(self):
36         params = {
37                     "header": {
38                         "appKey": self.appKey,
39                         "apiToken": self.apiToken,
40                         "requestId": self.requestId,
41                         "timestamp": self.timeSTAP,
42                         "sign": self.sign
43                     },
44                     "body": {
45                         "hotels": [
46                             "aaa",
47                             "bbb"
48                         ]
49                     }
50                 }
51         body_list1 = str(params["body"])
52         body_list2 = body_list1.replace(body_list1[25:32], "udid")
53         body_list3 = re.sub("[[]", "", body_list2)
54         body_list = re.sub("[]]", "", body_list3)
55         list_sig = self.timeSTAP + self.apiToken + self.appSecret + self.appKey + body_list
56         signature = hashlib.sha1(list_sig.encode('utf-8')).hexdigest().upper().strip()
57         params["header"]["sign"] = signature
58         res = requests.post(url=self.base_url+"/partner/hotel/remove", data=json.dumps(params), headers=self.headers)        #第二次簽名驗(yàn)證
59         response = json.loads(res.content.decode('utf-8'))
60         self.assertEqual(response["msg"], "SUCCESS")
61         
62             
63 if __name__ == '__main__':
64 mySuit = unittest.TestSuite()
65 tesTCases = ["test_001", "test_002", "test_003", "test_004", "test_005", "test_006", "test_007"]
66 for cs in tesTCases:
67     mySuit.addTest(Test(cs))
68 # mySuit.addTest(Test("test_003"))
69 myRun = unittest.TextTestRunner()
70 myRun.run(mySuit)    
復(fù)制代碼

另外,學(xué)會(huì)了一個(gè)變量名warning的處理辦法,pep8編碼規(guī)范,要求變量名或者函數(shù)名最好包含大小寫。

 

除了通過修改pycharm設(shè)置的方式,還可以使用“駝峰命名法”來給變量或函數(shù)命名。

“駝峰命名法”,顧名思義,就是變量或函數(shù)的命名要像駱駝的駝峰一樣有高低起伏(Ps:這個(gè)名字是不是很可愛呢~)

另外:python的hashlib庫(kù)還可以完成密碼的md5加密等功能。

如:

import hashlib

password1 = hashlib.md5("123456".encode('utf-8')).hexdigest().upper().strip()
print(password1)

結(jié)果:E10ADC3949BA59ABBE56E057F20F883E