先贴脚本, 大神请直取
-
新建
线程组 → http取样器 → 前置处理器 → bean shell 预处理程序
import org.apache.commons.codec.digest.DigestUtils; import java.util.Date; //没有第三方jar包,请放心 import Date date = new Date(); //将时间戳截取到秒的量级(长度共10位),大神可以考虑地板除,弟中弟请当没看见 String timestamp = String.valueOf(date.getTime()/1000); //将时间戳赋值给ts变量,方便以 ${ts} 的方式引用 vars.put("ts",timestamp); //此处的SPhone的值可以用csv参数化 String data = "{\"SPhone\":\"18662255783\",\"EType\":0}"; String key = "a323f9b6-1f04-420e-adb9-b06ty67b0e63"; String bsign = "z417App" + timestamp + data + key; //MD5加密赋值给sign变量 vars.put("sign",DigestUtils.md5Hex(bsign));
小白请从这里看起
REST api大多会传sign(签名)字段,各接口对sign的内容、方式可能不一样,但一般模式都是从接口的入参中选择部分内容组成一个字符串,然后再拼接一个
secretKey
或appKey
(秘钥,值固定), 最后对这个拼接起来的字符串进行MD5的运算, 将结果赋值给sign
; 完整规范的接口文档都会有sign的算法描述。
解题思路
-
找到签名算法:接口文档为准,secretKey 或 appKey 可以问开发要;
-
接口的传入的参数不改变,则sign的值就不变:利用这点可以先用接口测试工具(如postman)调试下接口, 在线MD5请移步站长工具;
-
接口手动调通了,接下来翻译到工具的脚本语言中:说的比较晦涩,继续往下看吧;
-
特别注意:如果待签名的内容中有中文,需要特别处理下!!踩过坑(这部分会在loadrunner如何处理sign的博客中记录,先挖个坑,以后填)。
接口入参示例
-
以下是一个传入手机号获取短信验证码的请求消息
{ "AppKey": "z417App", "AppVer": "1.0.0", "Data": "{\"SPhone\":\"18662255783\",\"EType\":0}", "DeviceName": "web", "DeviceType": "web", "Lang": "CN", "Sign": "8011fd9bdacd3372103053b43bef76e7", "TimeStamp": 1560584745 }
初步分析
-
这是一串json消息:post方式发送
-
里面套的Data字段的值有点像json:Data的值是个字符串,不是内层 json
-
SPhone的值是字符串格式的电话号码:可能是个动态变化的,得用参数化处理下,使它变化起来
-
sign应该就是今天的主角:sign的算法请仔细阅读接口文档,实在看不懂就问开发
-
TimeStamp时间戳:一个精确到秒的时间戳
确定可能会动态变换的入参
-
SPhone字段
-
Sign字段
-
TimeStamp字段
签名算法
AppKey + TimeStamp + Data + secretKey 的值拼接在一起组成一个新的字符串,对该字符串进行MD5(32位[小])的运算。(tps:站长工具)
-
举个不能吃的栗子:
① AppKey 的值是 "z417App"② TimeStamp 的值是 1560584745
③ Data 的值是 "{\"SPhone\":\"18662255783\",\"EType\":0}"
④ secretKey 的值是 "a323f9b6-1f04-420e-adb9-b06ty67b0e63"
⑤ 拼接成一个字符串:
z417App1560584745{\"SPhone\":\"18662255783\",\"EType\":0}a323f9b6-1f04-420e-adb9-b06ty67b0e63
⑥ MD5运算后的值:8011fd9bdacd3372103053b43bef76e7 → 就可以赋给Sign了
-
再举个不能吃的栗子:(其他签名算法)
从所有入参中筛选出参数的值不为空(null,"")的集合,将集合中的参数名按照ASCII码从小到大排序(像英语词典), 使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串(sign字段不参与拼接),假设是str1;在str1最后拼接上key=secretKey,得到更长的一个字符串,假设是str2。对str2进行MD5运算,再将得到的字符串转换为大写。
-
入参举例:
{ "appid": "wxd930u", "mch_id": 10100, "device_info": 100, "body": "{\"EType\":0}", "DeviceType": "", "nonce_str": "ibuaiVc", "sign": "CD198C36632A274C49E5F2F028FA257C", "source": null }
-
第一步:按字典序对有值的入参、按url键值对的方式进行拼接
String str1 = "appid=wxd930u&body={\"EType\":0}&device_info=100&mch_id=10100&nonce_str=ibuaiVc";
-
第二步:拼接API密钥(secretKey的值:a323f9b6):
String str2 = str1 + "&key=a323f9b6";
-
第三步:签名MD5(32位[大],可用站长工具)
/** * 注意java中有自带的MD5方法,是16位以16个元素的形式返回值,大神请自己写,弟中弟请用已有的jar * md5hex是以32位16进制的小写字符串形式返回 */ String sign = DigestUtils.md5Hex(str2).toUpperCase(); // 即 sign = "CD198C36632A274C49E5F2F028FA257C"
再看bean shell脚本
import org.apache.commons.codec.digest.DigestUtils; // DigestUtils类中有md5算法
import java.util.Date; // Date类中有获取当前时间戳方法
//没有第三方jar包,请放心 import
Date date = new Date(); // 创建Date对象,取名叫date
//将时间戳截取到秒的量级(长度共10位),大神可以考虑地板除,弟中弟请当没看见
String timestamp = String.valueOf(date.getTime()/1000);
//将时间戳赋值给ts变量,方便以 ${ts} 的方式引用
vars.put("ts",timestamp);
//此处的SPhone的值可以用csv参数化
String data = "{\"SPhone\":\"18662255783\",\"EType\":0}";
String key = "a323f9b6-1f04-420e-adb9-b06ty67b0e63"; // 要拼接的secretKey
String bsign = "z417App" + timestamp + data + key; // 拼接
//MD5加密后,赋值给sign变量
vars.put("sign",DigestUtils.md5Hex(bsign));
在http的取样器中使用 ts 和 sign 的值
{
"AppKey": "z417App",
"AppVer": "1.0.0",
"Data": "{\"SPhone\":\"18662255783\",\"EType\":0}",
"DeviceName": "web",
"DeviceType": "web",
"Lang": "CN",
"Sign": "${sign}",
"TimeStamp": ${ts}
}
问题思考
-
前面的例子是建立在request body不变的情况下才能成立,如果request body变化呢?比如对手机号(Sphone)做了参数化。处理方式有两种,请戳 jmeter处理动态的签名(sign)内容
-
欢迎交流指正