最近項目接口采用了RSA加密,之前的jmeter自動化腳本不能沿用了,我決定用beanshell來處理加密和解密完成自動化。
通過一天的折騰,加上開發大兄弟的援助,我這個java零基礎的測試小白把rsa的加密解密方法搞出來了,idea運行成功加密和解密,代碼如下:

1 1 package com.example.code.demo; 2 2 3 3 import org.apache.commons.codec.binary.Base64; 4 4 5 5 import javax.crypto.BadPaddingException; 6 6 import javax.crypto.Cipher; 7 7 import javax.crypto.IllegalBlockSizeException; 8 8 import java.io.ByteArrayOutputStream; 9 9 import java.io.IOException; 10 10 import java.nio.charset.Charset; 11 11 import java.nio.charset.StandardCharsets; 12 12 import java.security.KeyFactory; 13 13 import java.security.interfaces.RSAPrivateKey; 14 14 import java.security.interfaces.RSAPublicKey; 15 15 import java.security.spec.PKCS8EncodedKeySpec; 16 16 import java.security.spec.X509EncodedKeySpec; 17 17 18 18 public class RsaTool { 19 19 20 20 /** RSA最大加密明文大小 */ 21 21 private static final int MAX_ENCRYPT_BLOCK = 245; 22 22 private static final Charset CHARSET = StandardCharsets.UTF_8; 23 23 /** RSA最大解密密文大小 */ 24 24 private static final int MAX_DECRYPT_BLOCK = 256; 25 25 // 公鑰加密 26 26 public String encrypt(String str, String publicKey) throws Exception { 27 27 //base64編碼的公鑰 28 28 byte[] decoded = Base64.decodeBase64(publicKey); 29 29 RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded)); 30 30 //RSA加密 31 31 Cipher cipher = Cipher.getInstance("RSA"); 32 32 cipher.init(Cipher.ENCRYPT_MODE, pubKey); 33 33 byte[] data = doSegmentFinal(cipher, str.getBytes(CHARSET), MAX_ENCRYPT_BLOCK); 34 34 // String outStr = Base64.encodeBase64String(cipher.doFinal(str.getBytes(StandardCharsets.UTF_8))); 35 35 String outStr = Base64.encodeBase64String(data); 36 36 // String str = new String(outStr) 37 37 System.out.println("這是公鑰加密后的字符: "+outStr); 38 38 return outStr; 39 39 } 40 40 // 私鑰解密 41 41 public String decrypt(String str, String privateKey) throws Exception { 42 42 //64位解碼加密后的字符串 43 43 byte[] inputByte = Base64.decodeBase64(str.getBytes(StandardCharsets.UTF_8)); 44 44 //base64編碼的私鑰 45 45 byte[] decoded = Base64.decodeBase64(privateKey); 46 46 RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded)); 47 47 //RSA解密 48 48 Cipher cipher = Cipher.getInstance("RSA"); 49 49 cipher.init(Cipher.DECRYPT_MODE, priKey); 50 50 byte[] data = doSegmentFinal(cipher,inputByte, MAX_DECRYPT_BLOCK); 51 51 // String outStr = new String(cipher.doFinal(inputByte)); 52 52 String outStr = new String(data,StandardCharsets.UTF_8); 53 53 System.out.println("這是私鑰解密后的字符: "+outStr); 54 54 return outStr; 55 55 } 56 56 private static byte[] doSegmentFinal(Cipher cipher, byte[] data, int segmentLength) 57 57 throws IOException, BadPaddingException, IllegalBlockSizeException { 58 58 try (ByteArrayOutputStream out = new ByteArrayOutputStream()) { 59 59 int inputLen = data.length; 60 60 int offSet = 0; 61 61 byte[] cache; 62 62 int i = 0; 63 63 64 64 while (inputLen - offSet > 0) { 65 65 if (inputLen - offSet > segmentLength) { 66 66 cache = cipher.doFinal(data, offSet, segmentLength); 67 67 } else { 68 68 cache = cipher.doFinal(data, offSet, inputLen - offSet); 69 69 } 70 70 out.write(cache, 0, cache.length); 71 71 i++; 72 72 offSet = i * segmentLength; 73 73 } 74 74 return out.toByteArray(); 75 75 } 76 76 } 77 77 public String msg(){ 78 78 return "never mind scandal and liber"; 79 79 } 80 80 }
接下來先試試用jmeter引用這個java文件
哦豁,報錯了
去找下萬能的度娘,看看這是個啥問題.
百度了一大堆文章也只有一些無光痛癢的東西,所以我打算換個方式,用jmeter引用jar包再調用解密方法。
之前引用fasijson包解析過json數據,操作本身還是不算麻煩。
首先在idea里把項目打成jar包,然后把jar包路徑配置到jmeter的任務里。(也可以將jar包放到jmeter/bin/lib/ext目錄下直接引用。或者是修改properties文件,建立一個單獨的依賴包目錄,這類教程網上很多)
在beanshell取樣器中引用jar包里的類,調用靜態加密方法。
依然在報錯,沒有找到RsaTool類.這就讓人很難受了.
再次去拜訪度娘,看了十幾個帖子,依然沒找到有效辦法,我就在想是不是這個java文件本身有問題。
我創建java項目時使用了spring boot這個框架,對這個框架的機制我完全不了解,我擔心jemter沒有識別到這框架的結構,於是新建了一個相對簡潔方便一點的maven項目,
可是打包調用后依然遇到了以上問題。最后我決定返璞歸真,建立一個最初始的java項目試試。
欸!好像能行
方法能夠成功調用,返回數據也是正常的。看來rsa加密沒什么問題了,接下就是要根據接口文檔處理入參和出參了。
請求接口的參數的規范是每個請求中將所有字段一起加密,作為data的值進行請求。
{
"data": "加密后的密文"
}
我大概的請求思路是csv文檔記錄每一個字段的名字和值,自己再寫一個java方法把字段拼接為json字符串,將json字符串傳入rsa加密方法獲取加密后的str,使用vars.get方法將str加載到jmeter變量parameters中,在http的post請求中調用${parameter},即可進行加密后的請求。
響應接口的參數規范和請求類似,只是多加了兩個字段
{
"code": "0000",
"message": "成功",
"data": {
"verifyCode": "000000"
}
}
接口會有參數關聯,比如token和一些id。此時就需要從響應的data中拿到加密后的字符串,進行解析,拿到需要的數據。
大致思路是:用jemter自帶的json提取器,將data的值及加密后的字符穿作為變量提取出來。再傳入rsa解密方法,獲得真實的json響應數據。利用alibaba的fastjson包解析數據,提取目標字段的值用於下一步的傳參或者是斷言(此處存疑,csv中也有參數,以誰為准)。
然而jmeter拿到加密后的字符串后請求服務器又雙叒叕報錯了,這次是服務返回json格式錯誤。
我查看了請求的參數,發現請求參數的加密字符串部分特殊字符被url編碼了。我懷疑這個編碼會有影響,嘗試使用了
${__urldecode('${parameter}')}方法將參數解碼,然並卵。
哈哈哈哈哈哈哈哈哈,然后我嘗試了將參數寫在了【消息體數據】里,All Green,跑通了。
原來POST請求下面兩個填寫參數的地方是有區別的,在【參數】中填寫請求參數會構造出類似GET的請求,格式是這樣的:ulr?parameter1¶meter2,也就是我們平時在瀏覽器地址框中看到的鏈接地址,這樣當然會被URL編碼啊 kora!
我們來看看這兩種填寫參數的方式的區別:
在【參數】中填寫請求數據
在【消息體】中填寫請求數據
准備工作已經就緒,接下來打算讀取CSV中的變量進行參數化請求。

1 import main.java.RsaCode.Rsa; 2 log.info("+++something for nothing+++"); 3 //parameter是從csv中讀取出來的參數 ,格式為{"phone":"13611111111"} 4 String postData = Rsa.encrypt(${parameter},"${pubKey}"); 5 vars.put("postData",postData);
運行,穩定報錯
2019-12-17 15:02:08,415 ERROR o.a.j.u.BeanShellInterpreter: Error invoking bsh method: eval In file: inline evaluation of: ``//source("D:/docs/java_proj/RsaCode/src/main/java/RsaCode/Rsa.java"); import mai . . . '' Encountered "( {" at line 9, column 30.
看來是JSON格式中雙引號的問題,因為java字符串只能用雙引號表示,不像python可以使用單引號/雙引號/三引號。
查閱了資料,發現使用beanshell自帶的vars.put()方法可以避免這個問題。

1 import main.java.RsaCode.Rsa; 2 log.info("+++something for nothing+++"); 3 //parameter是從csv中讀取出來的參數 ,格式為{"phone":"13611111111"} 4 String enData = vars.get("parameter"); 5 String postData = Rsa.encrypt(enData,"${pubKey}"); 6 vars.put("postData",postData);
vars.put("變量名”)是將jemter線程中的變量加載到beanshell定義的變量中
在數據驅動的過程中又遇到了新的問題,有多行內容的csv文件,在jmeter線程組中僅執行一次,不能遍歷所有行的內容。CSV data set config 本身的設置應該是沒問題的,因為之前有成功的案例。