先貼腳本, 大神請直取
-
新建
線程組 → 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)內容
-
歡迎交流指正