匯智動(dòng)力-接口測(cè)試心得
一、背景
最近負(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ī)則如下:
1、參與簽名的參數(shù)為:apiToken+appKey+appSecret+timestamp+body(提取body中的所有參數(shù))
2、將格式化后的參數(shù)以字典序升序排列,拼接在一起
3、將第二步形成字符串獲取SHA1摘要
4、第三步獲得的字符串即為簽名參數(shù)
二、代碼實(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))
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()
測(cè)試用例:
測(cè)試用例用unittest框架來組織
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)
另外,學(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


