前情介绍:
接口的安全性一向都是开发过程中比较在意的一件事情。如何测试已经加密后的接口是现在测试开发人员常面对的一个问题。
预习知识:接口加密的测试
- 练习环境配置
- 实例1(UI自动化-百度搜索)
- 实例2(有效登录)
- 实例3(无效登录)
- 实例4 ( Appium )
- 实例5 ( 连接mysql数据库)
- 实例6 ( GET/POST请求)
- 实例7(接口API测试)
- 实例8 (数据驱动)
- 实例9 (行为驱动)
- Appium Error总结
- robotframework Error总结
测试需求:
测试系统中添加公告的接口,此接口采用了 签名加密的方式(API_KEY+Time)
测试设计:
测试 加密后的接口的方法其实和一般的接口测试是一致的。只是多了一步对传输的数据进行加密的过程。
用扩展库的方式来完成数据的加密。(扩展库的方式详情-TBD,简单的来说就是你可以自己写Python文件,然后RF可以直接调用你所写的Python文件)
Python中有很多库都可以完成md5加密的功能,此次选取的是hashlib库(提前自行安装:pip install hashlib)
上图中就是对sign_str进行一个md5加密的形式,md5加密是不可逆的,也就是说当你得到6648e929329e53e7a91c50ae685a88b5那一串东西的时候,你并不知道反编译回来可以是@signpassword, BUT,你可以猜测啊,如果你猜它的md5原值就是@signpassword,最后你拿到加密后的值为6648e929329e53e7a91c50ae685a88b5,Bingo,恭喜你猜对了。所以呢,md5严格意义上来说是一种对称加密的算法,不算特别安全,不过这里为了让大家初步了解接口加密测试的方式,我们就从这里开始学习啦~~~
测试实现:
1 编写一个md5Enc.py文件,里面包含两个函数
- get_sign 用来处理接口的签名加密
- get_time_Stamp 用来处理把字符串模式的时间转换为时间戳(这里是一个坑,因为RF中的get time本身得出来的时间是类似2017-07-24 11:47:20 格式的,而 此接口需要的time是时间戳格式)
2 RF中新建一个项目,在TestSuite层级上导入上面的md5Enc.py文件作为扩展库(记得.py的后缀名需要加上的)
2 编写一个返回值为200的正向接口测试的Testcase
第一行:获取当前时间,赋值给变量t_now。
第二行:调用扩展库中的get_time_Stamp函数,将字符串格式的时间转换成时间戳,赋值给变量t_now_stamp
第三行:打印出来看看值是否正常,主要用于调试
第四行:调用扩展库中的get_sign函数,将API_KEY+time的值进行md5加密
第五行:对报头content-Type赋值后构建为一个字典赋值给变量headers
第六行:将一个常量的时间赋值给变量start_time_str
第七行:将POST需要传输的数据构造成字典对象赋值给变量payload
第八行:将payload中的sign取出来查看(为了调试,不必要)
第九行:建立一个Session开始准备发送请求
第十行:发送Post请求,所带参数为之前所构造好的参数
第十一行:对请求返回的status code进行断言,正常应为200
第十二行:打印返回的值的json格式的内容
第十三行:将返回的json格式值赋值给dict
第十四行到第十九行:对返回的值依据之前的接口定义文档来进行断言
附代码(Robot TestCase)
*** Settings ***
Documentation A robot file to test the CryptAPI
Library RequestsLibrary
Library Collections
Library OperatingSystem
Library md5Enc.py
Library Signtest.py
*** Test Cases ***
AddEvent_Sign
${t_now} get time NOW #获取当前时间
${t_now_stamp} get_time_Stamp ${t_now}
log ${t_now_stamp}
${sign_md5} get_sign ${t_now_stamp} #调用函数
${headers} Create Dictionary Content-Type=application/x-www-form-urlencoded
${start_time_str} set variable 2017-07-20 12:00:00
${payload} Create Dictionary eid=13 name=iphone8releasemeeting limit=2000 address=NewYork start_time=${start_time_str}
... time=${t_now_stamp} sign=${sign_md5}
${sign_log} Get From Dictionary ${payload} sign
Create Session event http://127.0.0.1:8000/api
${r}= Post Request event /sec_add_event/ data=${payload} headers=${headers}
Should Be Equal As Strings ${r.status_code} 200
log ${r.json()}
${dict} Set variable ${r.json()}
#断言结果
${msg} Get From Dictionary ${dict} message
Should Be Equal ${msg} add event success
${sta} Get From Dictionary ${dict} status
${status} Evaluate int(200)
Should Be Equal ${sta} ${status}
Test
${message} add_event_success
Log ${message}
md5Enc.py(导入的文件需要和robot的项目在同一个文件夹内)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
import hashlib
import json
import time
class md5Enc(object):
def get_sign(self,t):
'''
Examples:
${sign_md5} | Get Sign |${t_now}
'''
keyMd5 = hashlib.md5()
api_key = "&Guest-Bugmaster"
sign_str = t + api_key
sign_bytes_utf8 = sign_str.encode(encoding="utf-8")
keyMd5.update(sign_bytes_utf8)
keyMd5 = keyMd5.hexdigest()
return keyMd5
def get_time_Stamp(self,t=''):
timeArray = time.strptime(t, "%Y-%m-%d %H:%M:%S")
timeStamp = int(time.mktime(timeArray))
timeStamp_str = str(timeStamp)
return timeStamp_str
#a=md5Enc.get_sign('sdf','2017-07-20 16:04:30')
CC先生说,扩展库可以说是RF中非常强大的一个功能,如果想对之前的自动化测试代码进行重构,又不想花费太多工夫的情况下,使用RF的扩展库是再方便不过的功能。不过这里有一个坑是,如果你的Class里面的函数是以test开头的可能添加会失败。(不要问我是怎么知道的。。。一个多小时的时间就耗费在这个上面了。。。。)