Jmeter beanshell編程實(shí)例

現(xiàn)在我們有這樣一個(gè)接口做為壓測對(duì)象

其使用HTTP協(xié)議進(jìn)行交互,使用RSA加密算法進(jìn)行加密傳輸,然后進(jìn)行密文報(bào)文反饋。

其請(qǐng)求報(bào)文體基礎(chǔ)格式為JSON,如下所示:

{

"format":"json",

"message":{

"head":{

"branchCode":"2110",

"channel":"BESK",

"timeStamp":"20180827105901487",

"transCode":"billQuery",

"transFlag":"01",

"transSeqNum":"BP180827105846210047"

},

"info":{

"epayCode":"VC-PAY2018080265602",

"input1":"123456",

"merchantId":"103881104410001",

"traceNo":"VC180827105846813462",

"userId":"1637206339848118"

}

}

}

我們無法用Jmeter的其它組件來實(shí)現(xiàn)報(bào)文體加密,因此引入Beanshell前置處理器將報(bào)文進(jìn)行加密,接收到返回后再引入Beanshell斷言解密返回報(bào)文并斷言測試結(jié)果,具體拆分步驟如下:

在線程組下加入HTTP取樣器

在取樣器下加入HTTP信息頭管理

在取樣器下加入BeanShell預(yù)處理器,并完成JSON格式報(bào)文組裝

在BeanShell處理器中完成報(bào)文體RSA加密驗(yàn)簽,使用Jmeter變量保存

配置HTTP取樣器使其使用上一步變量值發(fā)送加密報(bào)文

在取樣器下加入BeanShell斷言,解密返回報(bào)文并斷言結(jié)果

配置其它監(jiān)控器如查看結(jié)果樹和聚合報(bào)告等監(jiān)控測試結(jié)果

3、BeanShell實(shí)現(xiàn)

3.1、原始單元測試的java代碼:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

package?com.xxx.test.mock;


import?cn.hutool.core.codec.Base64;

import?cn.hutool.core.date.DatePattern;

import?cn.hutool.core.date.DateUtil;

import?cn.hutool.http.HttpResponse;

import?cn.hutool.http.HttpUtil;

import?cn.hutool.json.JSONObject;

import?com.cwl.gw.xxx.utils;

import?org.apache.commons.lang3.StringUtils;



public?void?querySiteInfo() {

????try{

????????JSONObject paramJson =?new?JSONObject();

????????JSONObject messageJson =?new?JSONObject();

????????JSONObject headJson =?new?JSONObject();

????????headJson.put("branchCode",?"2110");

????????headJson.put("channel",?"BESK");

????????headJson.put("timeStamp", DateUtil.format(new?Date(), DatePattern.PURE_DATETIME_MS_PATTERN));

????????headJson.put("transCode",?"billQuery");

????????headJson.put("transFlag",?"01");

????????headJson.put("transSeqNum",?"BP"?+ DateUtil.format(new?Date(), DatePattern.PURE_DATETIME_MS_PATTERN));


????????JSONObject infoJson =?new?JSONObject();

????????infoJson.put("epayCode",?"VC-PAY"?+ DateUtil.format(new?Date(), DatePattern.PURE_DATETIME_MS_PATTERN));

????????infoJson.put("merchantId",?"103881399990002");

????????infoJson.put("userId",?"103881399990002");

????????infoJson.put("input1",?"13000007");

????????infoJson.put("traceNo",?"VC"?+ DateUtil.format(new?Date(), DatePattern.PURE_DATETIME_MS_PATTERN));



????????messageJson.put("head", headJson);

????????messageJson.put("info", infoJson);


????????paramJson.put("format",?"json");

????????paramJson.put("message", messageJson);


????????String req = paramJson.toString().replaceAll(" ",?"");

????????System.out.println("請(qǐng)求參數(shù):"?+ req);

????????String base64 =?new?String(Base64.encode(req.getBytes("utf-8")));

????????String sign = RSAUtil.sign(req, keyFilePath, keyStorePass);

????????System.out.println("簽名:"?+ sign);

????????System.out.println("base64:"?+ base64);

????????System.out.println("reqContent:"?+ sign +?"||"?+ base64);

????????System.out.println("請(qǐng)求地址:"?+ querySiteInfoUrl);

????????HttpResponse resp = HttpUtil.createPost(querySiteInfoUrl).body(sign +?"||"?+ base64,?"text/plain").execute();


????????System.out.println("response:"?+ resp);

????????String context = resp.body().substring(resp.body().indexOf("||") +?2);

????????System.out.println("context:"?+ context);

????????System.out.println("body:"?+ StringUtils.toString(Base64.decode(context),?"utf-8"));


????????System.out.println("驗(yàn)簽:"?+ RSAUtil.verifySign(base64, sign,? certFilePath));

????}?catch(Exception e) {

????????e.printStackTrace();

????}


}

3.2、調(diào)用的RSAUtil原始方法:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

package?com.xxx.gw.xxxx.utils;


import?cn.hutool.core.codec.Base64;

import?org.apache.commons.lang3.StringUtils;


import?javax.servlet.http.HttpServletRequest;

import?javax.servlet.http.HttpServletResponse;

import?java.io.File;

import?java.io.FileInputStream;

import?java.io.IOException;

import?java.io.UnsupportedEncodingException;

import?java.security.KeyStore;

import?java.security.PrivateKey;

import?java.security.PublicKey;

import?java.security.Signature;

import?java.security.cert.CertificateFactory;

import?java.security.cert.X509Certificate;

import?java.util.Enumeration;


/**

?* 驗(yàn)簽和加簽工具類

?* @author VC

?*

?*/

public?class?RSAUtil {


????/**

?????* 加簽

?????* @param dataString 數(shù)據(jù)串

?????* @param keyFilePath 秘鑰文件路徑

?????* @param keyStorePass 秘鑰庫密碼

?????* @return

?????*/

????public?static?String sign(String dataString, String keyFilePath, String keyStorePass) {

????????String signatureString =?null;

????????try?{

????????????KeyStore ks = KeyStore.getInstance("PKCS12");

????????????FileInputStream fis =?new?FileInputStream(keyFilePath);

????????????char[] nPassword =?null;

????????????if?((keyStorePass ==?null) || keyStorePass.trim().equals("")) {

????????????????nPassword =?null;

????????????}?else?{

????????????????nPassword = keyStorePass.toCharArray();

????????????}

????????????ks.load(fis, nPassword);

????????????fis.close();

????????????System.out.println("【返回?cái)?shù)據(jù)加簽】keystore type:"?+ ks.getType());

????????????Enumeration<String> enums = ks.aliases();

????????????String keyAlias =?null;

????????????if?(enums.hasMoreElements()) {

????????????????keyAlias = (String) enums.nextElement();

????????????????System.out.println("【返回?cái)?shù)據(jù)加簽】keyAlias="?+ keyAlias);

????????????}

????????????System.out.println("【返回?cái)?shù)據(jù)加簽】is key entry:"?+ ks.isKeyEntry(keyAlias));

????????????PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);

????????????java.security.cert.Certificate cert = ks.getCertificate(keyAlias);

????????????PublicKey pubkey = cert.getPublicKey();

????????????System.out.println("【返回?cái)?shù)據(jù)加簽】cert class = "?+ cert.getClass().getName());

????????????//System.out.println("【返回?cái)?shù)據(jù)加簽】cert = " + cert);

????????????System.out.println("【返回?cái)?shù)據(jù)加簽】public key = "?+ pubkey);

????????????System.out.println("【返回?cái)?shù)據(jù)加簽】private key = "?+ prikey);

????????????// SHA1withRSA算法進(jìn)行簽名

????????????Signature sign = Signature.getInstance("SHA1withRSA");

????????????sign.initSign(prikey);

????????????byte[] data = dataString.getBytes("utf-8");

????????????byte[] dataBase= Base64.encode(data).getBytes();

????????????// 更新用于簽名的數(shù)據(jù)

????????????sign.update(dataBase);

????????????byte[] signature = sign.sign();

????????????signatureString =?new?String(Base64.encode(signature));

????????????System.out.println("【返回?cái)?shù)據(jù)加簽】signature is : "?+ signatureString);

????????}?catch?(Exception e) {

????????????System.out.println("返回?cái)?shù)據(jù)加簽】失敗??!"?+ e);

????????}

????????return?signatureString;

????}


????/**

?????* 驗(yàn)簽

?????* @param base64body

?????* @param sign

?????* @param certFilePath

?????* @return

?????*/

????public?static?boolean?verifySign(String base64body, String sign, String certFilePath) {

????????X509Certificate cert =?null;

//????? certFilePath = resourcesPath + certFilePath;


????????try?{

????????????CertificateFactory cf = CertificateFactory.getInstance("X.509");

????????????cert = (X509Certificate) cf.generateCertificate(new?FileInputStream(new?File(certFilePath)));

????????????PublicKey publicKey = cert.getPublicKey();

????????????String publicKeyString =?new?String(Base64.encode(publicKey.getEncoded()));

????????????System.out.println("【請(qǐng)求數(shù)據(jù)驗(yàn)簽】證書公鑰:"?+ publicKeyString);

????????????Signature verifySign = Signature.getInstance("SHA1withRSA");

????????????verifySign.initVerify(publicKey);

????????????// 用于驗(yàn)簽的數(shù)據(jù)

????????????verifySign.update(base64body.getBytes("utf-8"));

????????????boolean?flag = verifySign.verify(Base64.decode(sign));

????????????System.out.println("【請(qǐng)求數(shù)據(jù)驗(yàn)簽】驗(yàn)簽結(jié)果:"?+ flag);

????????????return?flag;

????????}?catch?(Exception e) {

????????????System.out.println("【請(qǐng)求數(shù)據(jù)驗(yàn)簽】驗(yàn)簽出異常:"?+ e);

????????}

????????return?false;

????}

3.3、使用BeanShell預(yù)處理器實(shí)現(xiàn)報(bào)文加密:

庫導(dǎo)入部分:

1

2

3

4

import?cn.hutool.json.JSONObject;

import?cn.hutool.core.date.DatePattern;

import?cn.hutool.core.codec.Base64;

import?java.io.FileInputStream;<br>import?java.security.*;

其中:

hutool是java常用的工具類庫,在BeanShell中也可以同樣導(dǎo)入引用;

而java.io和java.security是JDK中提供的基礎(chǔ)類庫,直接導(dǎo)入即可。

JSON報(bào)文組裝:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

JSONObject paramJson =?new?JSONObject();

JSONObject messageJson =?new?JSONObject();

JSONObject headJson =?new?JSONObject();

headJson.put("branchCode",?"2110");

headJson.put("channel",?"BESK");

headJson.put("timeStamp",?new?Date().getTime());

headJson.put("transCode",?"billQuery");

headJson.put("transFlag",?"01");

headJson.put("transSeqNum",?"VC"?+?new?Date().getTime());


JSONObject infoJson =?new?JSONObject();

infoJson.put("epayCode",?"VC-PAY"?+?new?Date().getTime());

infoJson.put("merchantId",?"103881399990002");

infoJson.put("userId",?"103881399990002");

infoJson.put("input1",?"13000007");

infoJson.put("traceNo",?"VC"?+?new?Date().getTime());



messageJson.put("head", headJson);//將headJson放入整體報(bào)文json中

messageJson.put("info", infoJson);//將infoJson放入整體報(bào)文json中


paramJson.put("format",?"json");

paramJson.put("message", messageJson);


String req = paramJson.toString().replaceAll(" ",?"");//去掉報(bào)文中多余空格

log.info("請(qǐng)求參數(shù):"?+ req);

可以看到,基本與原demo代碼保持一致,只是用Date().getTime()方法代替了原DatePattern.PURE_DATETIME_MS_PATTERN。主要是因?yàn)楹笳咴贐eanShell使用過程中出現(xiàn)了找不到方法的問題,簡單起見,由于原代碼只是獲取時(shí)間戳以實(shí)現(xiàn)訂單編號(hào)等的唯一的效果,所以直接用我熟知的可用方法替代掉了,對(duì)整體沒有太大影響。

RSA加密:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

String base64 =?new?String(cn.hutool.core.codec.Base64.encode(req.getBytes("utf-8")));


String signatureString =?null;

File file =?new?File(".//user-rsa.pfx");//文件路徑


try?{

KeyStore ks = KeyStore.getInstance("PKCS12");

FileInputStream fis =?new?FileInputStream(file);

char[] nPassword =?null;

if?((keyStorePass ==?null) || keyStorePass.trim().equals("")) {

nPassword =?null;

}?else?{

nPassword = keyStorePass.toCharArray();

}

ks.load(fis, nPassword);

fis.close();

log.info("【返回?cái)?shù)據(jù)加簽】keystore type:"?+ ks.getType());

Enumeration enums = ks.aliases();

String keyAlias =?null;

if?(enums.hasMoreElements()) {

keyAlias = (String) enums.nextElement();

log.info("【返回?cái)?shù)據(jù)加簽】keyAlias="?+ keyAlias);

}

log.info("【返回?cái)?shù)據(jù)加簽】is key entry:"?+ ks.isKeyEntry(keyAlias));

PrivateKey prikey = (PrivateKey) ks.getKey(keyAlias, nPassword);

java.security.cert.Certificate cert = ks.getCertificate(keyAlias);

PublicKey pubkey = cert.getPublicKey();

log.info("【返回?cái)?shù)據(jù)加簽】cert class = "?+ cert.getClass().getName());

log.info("【返回?cái)?shù)據(jù)加簽】public key = "?+ pubkey);

log.info("【返回?cái)?shù)據(jù)加簽】private key = "?+ prikey);

Signature sign = Signature.getInstance("SHA1withRSA");

sign.initSign(prikey);

byte[] data = req.getBytes("utf-8");

byte[] dataBase= Base64.encode(data).getBytes();// 更新用于簽名的數(shù)據(jù)

sign.update(dataBase);

byte[] signature = sign.sign();

signatureString =?new?String(cn.hutool.core.codec.Base64.encode(signature));

log.info("【返回?cái)?shù)據(jù)加簽】signature is : "?+ signatureString);

}?catch?(Exception e) {

log.info("返回?cái)?shù)據(jù)加簽】失敗??!"?+ e);

}


log.info("簽名:"?+ signatureString);

log.info("base64:"?+ base64);

log.info("reqContent:"?+ sign +?"||"?+ base64);

log.info("請(qǐng)求地址:"?+ querySiteInfoUrl);

String params = signatureString +?"||"?+ base64;<br><br>//將加密后的字符串存入jmeter變量

vars.put("nhzdParams",params);

vars.put("base64",base64);

vars.put("sign",signatureString);

可以看到在BeanShell中,將原有的方法進(jìn)行了一些改寫。由于BeanShell語法和java很大程度上是通用的,因此改寫幅度較小。

此處我們是直接將原RSAUtil類中的邏輯直接寫入了BeanShell而非采用外部依賴的方式,實(shí)際也可以采取將原工具類打成外部包進(jìn)行引用。

使用log.info輸出Jmeter日志,可以使用log view實(shí)時(shí)查看,也可以在jmeter.log中查看到。

完成了對(duì)原始Json的加密后,使用vars.put將加密字串存入jmeter變量以便后續(xù)使用。

3.4、取樣器中發(fā)送請(qǐng)求:



3.4、使用BeanShell斷言器實(shí)現(xiàn)報(bào)文驗(yàn)簽和斷言:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

import?cn.hutool.json.JSONObject;

import?cn.hutool.core.date.DatePattern;

import?cn.hutool.core.codec.Base64;

import?java.io.FileInputStream;

import?java.security.*;

import?org.apache.commons.lang3.StringUtils;




X509Certificate cert =?null;

  String base64 = vars.get("base64");

  String sign = vars.get("sign");


  try?{

????CertificateFactory cf = CertificateFactory.getInstance("X.509");

????cert = (X509Certificate) cf.generateCertificate(new?FileInputStream(new?File(".//public-rsa.cer")));

????PublicKey publicKey = cert.getPublicKey();

????String publicKeyString =?new?String(Base64.encode(publicKey.getEncoded()));

????log.info("【請(qǐng)求數(shù)據(jù)驗(yàn)簽】證書公鑰:"?+ publicKeyString);

????Signature verifySign = Signature.getInstance("SHA1withRSA");

????erifySign.initVerify(publicKey);

?// 用于驗(yàn)簽的數(shù)據(jù)

????verifySign.update(base64.getBytes("utf-8"));

????boolean flag = verifySign.verify(Base64.decode(sign));

????log.info("【請(qǐng)求數(shù)據(jù)驗(yàn)簽】驗(yàn)簽結(jié)果:"?+ flag);

}?catch?(Exception e) {

????log.info("【請(qǐng)求數(shù)據(jù)驗(yàn)簽】驗(yàn)簽出異常:"?+ e);

  }

1

2

3

4

5

String resp=new?String(prev.getResponseData());

String context = resp.substring(resp.indexOf("||") + 2);

String result = StringUtils.toString(Base64.decode(context),"utf-8");


log.info("系統(tǒng)返回的結(jié)果是:"?+ result);

1

2

3

4

5

6

7

8

9

<br>

if(result.contains("查詢成功")){

  Failure=false;

  FailureMessage="斷言成功";

  log.info("斷言成功");

  }else{

????Failure=true;

????FailureMessage="斷言失敗";

????log.info("斷言失敗");<br>  }

外部引用和代碼格式部分不再贅述,此組件中主要的邏輯是:

取出上一節(jié)預(yù)處理組件中存入的base64和sign兩個(gè)字符串變量,使用預(yù)設(shè)邏輯進(jìn)行驗(yàn)簽。

通過prev.getResponseData()方法獲得返回報(bào)文,進(jìn)行解密,然后根據(jù)返回中是否存在“查詢成功”關(guān)鍵字進(jìn)行斷言。

到這里BeanShell的應(yīng)用就基本結(jié)束了,我們已經(jīng)實(shí)現(xiàn)了 組裝報(bào)文-加密-發(fā)送報(bào)文-接收返回-解密-斷言 這一系列的工作。

其它常用組件的搭配使用暫略。

深圳網(wǎng)站建設(shè)www.sz886.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容