先贴脚本, 大神请直取
新建线程组 → 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)内容
欢迎交流指正