0


JAVA面试题分享四百一十六:使用Postman和JMeter实现接口签名操作

一、前言

有些接口的请求会带上

sign

(签名)进行请求,各接口对

sign

的签名内容、方式可能不一样,但一般都是从接口的入参中选择部分内容组成一个字符串,然后再进行签名操作, 将结果赋值给

sign

; 完整规范的接口文档都会有

sign

的算法描述。

这里通过

Postman

Pre-request Script

以及

JMeter

BeanShell

前置处理器进行接口签名的处理。

完整代码在每部分的最后

被测系统

teachSignServer

Gitee:https://gitee.com/z417/knowledgebroadcast/tree/master/teachSignServer-tools

直接双击运行

.exe

文件即可(密钥文件与

conf.ini

需要与

exe

处于同一文件夹下)

其余工具:

1.bundle.jshttps://github.com/joolfe/postman-util-lib/tree/master/postman

使用方式我们在后面使用到了再进行介绍

2.json.jar: https://mvnrepository.com/artifact/org.json/json

选择适合的版本

点击

bundle

将下载的

jar

包置于

jmeter

./lib/ext

下并重启

jmeter

被测接口信息:
接口URLMethodBody签名规则v0http://127.0.0.1:5000/api/v0/teachsignPOST{
"AppKey": "z417App",
"AppVer": "1.0.0",
"Data": "{"SPhone":"18662255783","EType":0}",
"DeviceName": "web",
"DeviceType": "web",
"Lang": "CN",
"Sign": "teachsign",
"TimeStamp": 1625456804
}appkey,timestamp,data,secret四个字段的值拼接,使用32位md5进行签名v1http://127.0.0.1:5000/api/v1/teachsignPOST{
"appid": "wxd930u",
"mch_id": 10100,
"device_info": 100,
"body": "{"EType":0}",
"DeviceType": "",
"nonce_str": "ibuaiVc",
"sign": "CD198C36632A274C49E5F2F028FA257C",
"source": null,
"ts": 1625456804
}1. 参与签名运算的参数选用入参里边value非空的参数
2. 参与签名运算的参数按照ASCII顺序排序
3. 组合方式:key=value通过&符连接
4. 最后加上盐key=secret(secret在conf.ini中配置,同理后面的私钥与公钥也可在其中进行配置)
5. 使用32位md5进行签名,sign的字母全大写v2http://127.0.0.1:5000/api/v2/teachsignPOST{
"busId": "",
"busCnl": "POS",
"requJnINo": "abceefgghkjlafksdffdsf",
"reqTxnTm": "16:30:16",
"serviceCode": "chengxusong",
"bussJnIno": "Arabic - Bahrain",
"sign": "fsdfsd",
"reqTxnDt": "20210907",
"nonceStr": "Language",
"sysCnl": "OKPOS",
"ts": 1631003416
}1. 参与签名运算的参数选用入参里边value非空的参数
2. 参与签名运算的参数按照ASCII顺序排序
3. 使用private_key签名
4. 使用SHA256withRSA进行签名

二、v0接口

1.0 Postman

获取请求参数并将

body

的参数转换为

json

对象

var Json = JSON.parse(pm.request.body);

获取所需变量并将新的时间戳更新到

json

对象中

var TimeStamp = Date.parse(new Date()) / 1000 - 10;Json.TimeStamp = TimeStamp;var AppKey = Json.AppKey;var Data = Json.Data;var secretKey = "a323f9b6-1f04-420e-adb9-b06ty67b0e63";

字符串拼接

var str = AppKey + TimeStamp + Data + secretKey;

进行

md5

运算并将生成的

hash

序列转换为字符串

var strmd5= CryptoJS.MD5(str).toString();

修改

json

对象中

sign

并将

md5

对象写回

body

Json.Sign = strmd5; pm.request.body.raw = JSON.stringify(Json);  // 将修改后的JSON转换回字符串格式写回到请求体中

完整代码:

/*   vo加密规则:  appkey,timestamp,data,secret四个字段的值拼接,使用32位md5加密*//* * 获取请求参数 */var Json = JSON.parse(pm.request.body);    // 将body的参数转换为json对象 /* * 获取所需变量 */var TimeStamp = Date.parse(new Date()) / 1000 - 10;   // 获取时间戳Json.TimeStamp = TimeStamp;   // 修改JSONvar AppKey = Json.AppKey;var Data = Json.Data;var secretKey = "a323f9b6-1f04-420e-adb9-b06ty67b0e63";/*  * 拼接字符串并加密 */var str = AppKey + TimeStamp + Data + secretKey;var strmd5= CryptoJS.MD5(str).toString();   // 调用方法进行md5运算并将生成的hash序列转换为字符串Json.Sign = strmd5;   // 修改JSONpm.request.body.raw = JSON.stringify(Json);  // 将修改后的JSON写回到请求体中

2.0 JMeter

JMeter

的时间戳可以直接使用

JMeter

自带函数在

body

中获取,当然也可以在

BeanShell

前置处理器中使用代码获取

/1000

是因为

JMeter

默认生成的时间戳为13位时间戳,我们只需要10位即可。

导包(

org.json

为第三方

jar

包)

import org.apache.commons.codec.digest.DigestUtils;import org.apache.jmeter.config.*;import org.json.*;

获取请求传入的

body

,将其转化为

Json

对象

// 获取请求Arguments arguments = sampler.getArguments();// 获取请求中的body内容Argument arg = arguments.getArgument(0);// 获取body的value,并将其转化为JSONObject对象JSONObject dataobj = new JSONObject(arg.getValue());

获取变量并拼接

String TimeStamp = dataobj.optString("TimeStamp");String AppKey = dataobj.optString("AppKey");String Data = dataobj.optString("Data");String secretKey = "a323f9b6-1f04-420e-adb9-b06ty67b0e63";String str = AppKey + TimeStamp + Data + secretKey;

进行

md5

运算

sign = DigestUtils.md5Hex(str);

修改

json

对象的

sign

并转换为字符串写回

body

dataobj.put("Sign", sign);  // 修改Signarg.setValue(dataobj.toString());  // 转换为字符串并歇回request-body中

完整代码:

import org.apache.commons.codec.digest.DigestUtils;import org.apache.jmeter.config.*;import org.json.*;/* * 获取请求传入的body,将其转化为Json对象 */// 获取请求Arguments arguments = sampler.getArguments();// 获取请求中的body内容Argument arg = arguments.getArgument(0);// 获取body的value,并将其转化为JSONObject对象JSONObject dataobj = new JSONObject(arg.getValue());/* * 获取变量并拼接字符串 */// 获取变量String TimeStamp = dataobj.optString("TimeStamp");String AppKey = dataobj.optString("AppKey");String Data = dataobj.optString("Data");String secretKey = "a323f9b6-1f04-420e-adb9-b06ty67b0e63";// 字符串拼接String str = AppKey + TimeStamp + Data + secretKey;/* * 签名,更新body */sign = DigestUtils.md5Hex(str);  // md5运算dataobj.put("Sign", sign);  // 修改Signarg.setValue(dataobj.toString());  // 转换为字符串并写回request-body中

三、v1接口

1.0 Postman

获取请求参数并将

body

的参数转换为

json

对象

var Json = JSON.parse(pm.request.body);

获取时间戳并修改

json

对象

var ts = Date.parse(new Date()) / 1000 - 10;Json.ts = ts;

去除

sign

参数本身,然后去除值是空的参数

var keys = [];// 循环遍历JSONfor (let k in Json ){    // 排除json中键位sign以及值为空的数据    if (k == 'sign' || !Json[k]){        continue;    }    keys.push(k);  // 生成筛选后的key序列}

排序

keys.sort();

拼接字符串

let keys_str = '';for (let x of keys){    keys_str += `${x}=${Json[x]}&`;  // 使用模版字符串进行拼接}keys_str = keys_str + "key=a323f9b6-1f04-420e-adb9-b06ty67b0e63";

进行

md5

运算并将生成的

hash

序列转换为字母全大写字符串

var strmd5= CryptoJS.MD5(keys_str).toString().toUpperCase();

修改

json

对象中

sign

并将

md5

对象写回

body

Json.sign = strmd5;pm.request.body.raw = JSON.stringify(Json);

完整代码:

/*    v1加密规则:    1. 参与签名运算的参数选用入参里边value非空的参数    2. 参与签名运算的参数按照ASCII顺序排序    3. 组合方式:key=value通过&符连接    4. 最后加上key=secret    5. 使用32位md5签名,sign的字母全大写*//* * 获取请求参数 */var Json = JSON.parse(pm.request.body);var ts = Date.parse(new Date()) / 1000 - 10;  // 获取时间戳Json.ts = ts;  // 修改json/* * 去除sign参数本身,然后去除值是空的参数 */var keys = []; // 定义key序列// 循环遍历JSONfor (let k in Json ){    // 排除json中键位sign以及值为空的数据    if (k == 'sign' || !Json[k]){        continue;    }    keys.push(k);  // 生成筛选后的key序列}/* * 对请求参数排序 */keys.sort();/* * 拼接字符串 */let keys_str = '';for (let x of keys){    keys_str += `${x}=${Json[x]}&`;  // 使用模版字符串进行拼接}keys_str = keys_str + "key=a323f9b6-1f04-420e-adb9-b06ty67b0e63";/* * 签名并更新body */var strmd5= CryptoJS.MD5(keys_str).toString().toUpperCase();  // 调用方法进行md5运算并将生成的hash序列转换为字母全大写字符串Json.sign = strmd5;  // 修改Jsonpm.request.body.raw = JSON.stringify(Json);

2.0 JMeter

同样在

body

中使用内置函数定义时间戳

ts

导包

import org.apache.commons.codec.digest.DigestUtils; import org.apache.jmeter.config.*;import org.json.*;

获取请求传入的

body

,将其转化为

Json

对象

// 获取请求Arguments arguments = sampler.getArguments();// 获取请求中的body内容Argument arg = arguments.getArgument(0);// 获取body的value,并将其转化为JSONObject对象JSONObject dataobj = new JSONObject(arg.getValue());

获取

Json

key
// 创建list存储body中的key值List keyArry = new ArrayList();// 生成迭代对象Iterator iterator = dataObj.keys(); // 循环key,将其放入listfor (String key : iterator) {  if (!key.equals("sign") && !key.equals("Sign")) {    keyArry.add(key);  }}

list

进行排序

Collections.sort(keyArry);

字符串拼接

String str = "";for (String s : keyArry) {//  log.error(s);  String value = dataObj.optString(s);    // 剔除值为空或值为null的参数  if (!value.equals("") && !value.equals(null)) {    str = str+s+"="+ value+"&";  }}str = str + "key=a323f9b6-1f04-420e-adb9-b06ty67b0e63";

进行

md5

运算并转换为字母全大写

String sign = DigestUtils.md5Hex(str).toUpperCase();

修改

json

对象的

sign

并转换为字符串写回

body

dataobj.put("sign", sign);  // 修改Signarg.setValue(dataobj.toString());  // 转换为字符串并歇回request-body中

完整代码:

import org.apache.commons.codec.digest.DigestUtils; import org.apache.jmeter.config.*;import org.json.*;/* * 获取请求传入的body,将其转化为Json对象 */// 获取请求Arguments arguments = sampler.getArguments();// 获取请求中的body内容Argument arg = arguments.getArgument(0);// 获取body的value,并将其转化为JSONObject对象JSONObject dataObj = new JSONObject(arg.getValue()); /* * 获取Json的key进行排序 */// 创建list存储body中的key值List keyArry = new ArrayList();// 生成迭代对象Iterator iterator = dataObj.keys(); // 循环key,将其放入listfor (String key : iterator) {  if (!key.equals("sign") && !key.equals("Sign")) {    keyArry.add(key);  }}/* * 对list进行排序 */Collections.sort(keyArry);/* * 循环list中的key,读取对应的Value组成字符串 */String str = "";for (String s : keyArry) {  String value = dataObj.optString(s);    // 剔除值为空或值为null的参数  if (!value.equals("") && !value.equals(null)) {    str = str+s+"="+ value+"&";  }}str = str + "key=a323f9b6-1f04-420e-adb9-b06ty67b0e63";/* * 签名并更新body */String sign = DigestUtils.md5Hex(str).toUpperCase();  // 进行md5运算并转换为字母全大写dataobj.put("sign", sign);  // 修改Signarg.setValue(dataobj.toString());  // 转换为字符串并写回request-body中

四、v2接口

1.0 Postman

将下载后的

json

导入到

postman

,进入

Lib install

请求

发送请求,该请求会将

bundle.js

写入到全局变量中

获取请求参数并将

body

的参数转换为

json

对象

var Json = JSON.parse(pm.request.body);

获取时间戳并修改

json

对象

var ts = Date.parse(new Date()) / 1000 - 10;Json.ts = ts;

去除

sign

参数本身,然后去除值是空的参数

var keys = [];// 循环遍历JSONfor (let k in Json ){    // 排除json中键位sign以及值为空的数据    if (k == 'sign' || !Json[k]){        continue;    }    keys.push(k);  // 生成筛选后的key序列}

排序

keys.sort();

拼接字符串

let keys_str = '';for (let x of keys){    keys_str += `${x}=${Json[x]}&`;  // 使用模版字符串进行拼接}keys_str = keys_str.slice(0,-1);  // 删除最后一个&

导入刚才写入到全局变量的js

eval(pm.globals.get("pmlib_code"));

由于私钥过长,所以这里把私钥的内容写到环境变量中,私钥内容可在

pkcs8_rsa_private_key.pem

查看(私钥与公钥可自行更换,在

conf.ini

中进行配置即可)

获取私钥

const privatekey = pm.environment.get("privatekey");

加密

const sha256withrsa = new pmlib.rs.KJUR.crypto.Signature({"alg": "SHA256withRSA"});  // 生成签名对象并制定为SHA256withRSA类型sha256withrsa.init(privatekey);  // 初始化privatekeysha256withrsa.updateString(keys_str);  // 更新要签名的数据const sign = pmlib.rs.hextob64(sha256withrsa.sign());  // 签名并转换为Base64字符串

修改

json

对象中

sign

并将

md5

对象写回

body

Json.sign = sign; pm.request.body.raw = JSON.stringify(Json);  // 将修改后的JSON转换回字符串格式写回到请求体中

完整代码:

/*    v2加密规则:    1. 参与签名运算的参数选用入参里边value非空的参数    2. 参与签名运算的参数按照ASCII顺序排序    3. 使用private_key签名    4. 使用SHA256withRSA进行签名*//* * 获取请求参数 */var Json = JSON.parse(pm.request.body);var ts = Date.parse(new Date()) / 1000 - 10;  // 获取时间戳Json.ts = ts;  // 修改json/* * 去除sign参数本身,然后去除值是空的参数 */var keys = []; // 定义key序列// 循环遍历JSONfor (let k in Json ){    // 排除json中键位sign以及值为空的数据    if (k == 'sign' || !Json[k]){        continue;    }    keys.push(k);  // 生成筛选后的key序列}/* * 对请求参数排序 */keys.sort();/* * 拼接字符串 */let keys_str = '';for (let x of keys){    keys_str += `${x}=${Json[x]}&`;  // 使用模版字符串进行拼接}keys_str = keys_str.slice(0,-1);  // 删除最后一个&/* * 加密并更新body */eval(pm.globals.get("pmlib_code"));  // 导入写入到全局变量的jsconst privatekey = pm.environment.get("privatekey");  // 从环境变量获取私钥const sha256withrsa = new pmlib.rs.KJUR.crypto.Signature({"alg": "SHA256withRSA"});  // 生成签名对象并制定为SHA256withRSA类型sha256withrsa.init(privatekey);  // 初始化privatekeysha256withrsa.updateString(keys_str);  // 更新要签名的数据const sign = pmlib.rs.hextob64(sha256withrsa.sign());  // 签名并转换为Base64字符串Json.sign = sign;pm.request.body.raw = JSON.stringify(Json);

2.0 JMeter

同样在

body

中使用内置函数定义时间戳

ts

,同时添加用户定义的变量配置元件来存放私钥

导包

import org.apache.jmeter.config.*;import org.apache.commons.codec.digest.DigestUtils; import org.json.*;import java.security.*;import java.security.spec.PKCS8EncodedKeySpec;

获取请求传入的

body

,将其转化为

Json

对象

// 获取请求Arguments arguments = sampler.getArguments();// 获取请求中的body内容Argument arg = arguments.getArgument(0);// 获取body的value,并将其转化为JSONObject对象JSONObject dataobj = new JSONObject(arg.getValue());

获取

Json

key
// 创建list存储body中的key值List keyArry = new ArrayList();// 生成迭代对象Iterator iterator = dataObj.keys(); // 循环key,将其放入listfor (String key : iterator) {  if (!key.equals("sign") && !key.equals("Sign")) {    keyArry.add(key);  }}

list

进行排序

Collections.sort(keyArry);

字符串拼接

String str = "";for (String s : keyArry) {//  log.error(s);  String value = dataObj.optString(s);    // 剔除值为空或值为null的参数  if (!value.equals("") && !value.equals(null)) {    str = str+s+"="+ value+"&";  }}//删除最后一个&str = str.substring(0,str.length()-1);

读取私钥

java中读取私钥需要删除前面的“

-----BEGIN PRIVATE KEY-----

”和后面的“

-----END PRIVATE KEY-----

”,且需要

key

首尾连接中间无换行或空格。

String privateKeyString = vars.get("privateKey");  // 从用户定义的变量中读取私钥privateKeyString = privateKeyString.replace(" ", "");  // 删除多余的空格 byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);  // 将Base64解码转化为字符串PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);  // 根据给定的编码密钥创建一个新的 PKCS8EncodedKeySpecPrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);  // 生成RSA的私钥对象。

创建

Signature

对象并初始化

Signature signature = Signature.getInstance("SHA256withRSA");  // 生成SHA256withRSA的Signature 对象signature.initSign(privateKey);  // // 初始化签署签名的私钥

更新要签名的数据

signature.update(str.getBytes("UTF-8"));  // 更新要签名或验证的字节

签名

byte[] signatureBytes = signature.sign();  // 执行签名String sign = Base64.getEncoder().encodeToString(signatureBytes);  // 将签名结果转换为 Base64 字符串

修改

json

对象的

sign

并转换为字符串写回

body

dataobj.put("sign", sign);  // 修改Signarg.setValue(dataobj.toString());  // 转换为字符串并歇回request-body中

完整代码:

import org.apache.jmeter.config.*;import org.apache.commons.codec.digest.DigestUtils; import org.json.*;import java.security.*;import java.security.spec.PKCS8EncodedKeySpec;/* * 获取请求传入的body,将其转化为Json对象 */// 获取请求Arguments arguments = sampler.getArguments();// 获取请求中的body内容Argument arg = arguments.getArgument(0);// 获取body的value,并将其转化为JSONObject对象JSONObject dataObj = new JSONObject(arg.getValue()); /* * 获取Json的key进行排序 */// 创建list存储body中的key值List keyArry = new ArrayList();// 生成迭代对象Iterator iterator = dataObj.keys(); // 循环key,将其放入listfor (String key : iterator) {  if (!key.equals("sign") && !key.equals("Sign")) {    keyArry.add(key);//    log.error(key);  }}// 对list进行排序Collections.sort(keyArry);/* * 循环list中的key,读取对应的Value组成字符串 */String str = "";for (String s : keyArry) {//  log.error(s);  String value = dataObj.optString(s);  if (!value.equals("")) {    str = str+s+"="+ value+"&";  }}//删除最后一个&str = str.substring(0,str.length()-1);/* * 读取私钥 */String privateKeyString = vars.get("privateKey");  // 从用户定义的变量中读取私钥privateKeyString = privateKeyString.replace(" ", "");  // 删除多余的空格 byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);  // 将Base64解码转化为字符串PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);  // 根据给定的编码密钥创建一个新的 PKCS8EncodedKeySpecPrivateKey privateKey = KeyFactory.getInstance("RSA").generatePrivate(keySpec);  // 生成RSA的私钥对象。/* * 读创建 Signature 对象并初始化 */Signature signature = Signature.getInstance("SHA256withRSA");  // 生成SHA256withRSA的Signature 对象signature.initSign(privateKey);  // 初始化签署签名的私钥  // /* * 更新要签名的数据化 */signature.update(str.getBytes("UTF-8"));  // 更新要签名或验证的字节/* * 签名并更新body */byte[] signatureBytes = signature.sign();  // 签署所有更新字节的签名// 将签名结果转换为 Base64 字符串String sign = Base64.getEncoder().encodeToString(signatureBytes);  // 编码为Base64字符串dataobj.put("sign", sign);  // 修改Signarg.setValue(dataobj.toString());  // 转换为字符串并歇回request-body中
标签: java postman jmeter

本文转载自: https://blog.csdn.net/qq_45038038/article/details/135938930
版权归原作者 之乎者也· 所有, 如有侵权,请联系我们删除。

“JAVA面试题分享四百一十六:使用Postman和JMeter实现接口签名操作”的评论:

还没有评论