【PostmanJMeter】使用Postman和JMeter进行signature签名

news2024/9/22 1:44:59

一、前言

​ 有些接口的请求会带上sign(签名)进行请求,各接口对sign的签名内容、方式可能不一样,但一般都是从接口的入参中选择部分内容组成一个字符串,然后再进行签名操作, 将结果赋值给sign; 完整规范的接口文档都会有sign的算法描述。这里通过Postman的Pre-request Script以及JMeter的BeanShell前置处理器进行接口签名的处理。(完整代码在每部分的最后)

被测系统teachSignServer:

Gitee:江苏豪之诺软件科技有限公司/KnowledgeBroadcast - Gitee.com

直接双击运行.exe文件即可(密钥文件与conf.ini需要与exe处于同一文件夹下)

其余工具:

1.bundle.js:postman-util-lib/postman at master · joolfe/postman-util-lib · GitHub

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

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

选择适合的版本

点击bundle

将下载的jar包置于jmeter的./lib/ext下并重启jmeter

被测接口信息:

接口

URL

Method

Body

签名规则

v0

http://127.0.0.1:5000/api/v0/teachsign

POST

{"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进行签名

v1

http://127.0.0.1:5000/api/v1/teachsign

POST

{"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的字母全大写

v2

http://127.0.0.1:5000/api/v2/teachsign

POST

{"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.Postman

获取请求参数并将body的参数转换为json对象

javascript

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

获取所需变量并将新的时间戳更新到json对象中

javascript

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";

字符串拼接

javascript

var str = AppKey + TimeStamp + Data + secretKey;

进行md5运算并将生成的hash序列转换为字符串

javascript

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

修改json对象中sign并将md5对象写回body中

javascript

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

完整代码:

javascript

/* 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; // 修改JSON var 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; // 修改JSON pm.request.body.raw = JSON.stringify(Json); // 将修改后的JSON写回到请求体中
2.JMeter

在JMeter的时间戳可以直接使用JMeter自带函数在body中获取,当然也可以在BeanShell前置处理器中使用代码获取

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

导包(org.json为第三方jar包)

java

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

获取请求传入的body,将其转化为Json对象

java

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

获取变量并拼接

java

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运算

java

sign = DigestUtils.md5Hex(str);

修改json对象的sign并转换为字符串写回body中

java

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

完整代码:

java

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); // 修改Sign arg.setValue(dataobj.toString()); // 转换为字符串并写回request-body中

三、v1接口

1.Postman

​ 获取请求参数并将body的参数转换为json对象

javascript

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

获取时间戳并修改json对象

javascript

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

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

javascript

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

排序

javascript

keys.sort();

拼接字符串

javascript

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序列转换为字母全大写字符串

javascript

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

修改json对象中sign并将md5对象写回body中

javascript

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

完整代码:

javascript

/* 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序列 // 循环遍历JSON for (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; // 修改Json pm.request.body.raw = JSON.stringify(Json);
2.JMeter

同样在body中使用内置函数定义时间戳ts

导包

java

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

获取请求传入的body,将其转化为Json对象

java

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

获取Json的key

java

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

对list进行排序

java

Collections.sort(keyArry);

字符串拼接

java

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运算并转换为字母全大写

java

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

修改json对象的sign并转换为字符串写回body中

java

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

完整代码:

java

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,将其放入list for (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); // 修改Sign arg.setValue(dataobj.toString()); // 转换为字符串并写回request-body中

四、v2接口

1.Postman

将下载后的json导入到postman,进入Lib install请求

发送请求,该请求会将bundle.js写入到全局变量中

获取请求参数并将body的参数转换为json对象

javascript

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

获取时间戳并修改json对象

javascript

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

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

javascript

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

排序

javascript

keys.sort();

拼接字符串

javascript

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

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

javascript

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

由于私钥过长,所以这里把私钥的内容写到环境变量中,私钥内容可在pkcs8_rsa_private_key.pem查看(私钥与公钥可自行更换,在conf.ini中进行配置即可)

获取私钥

javascript

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

加密

javascript

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

修改json对象中sign并将md5对象写回body中

javascript

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

完整代码:

javascript

/* 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序列 // 循环遍历JSON for (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")); // 导入写入到全局变量的js const privatekey = pm.environment.get("privatekey"); // 从环境变量获取私钥 const sha256withrsa = new pmlib.rs.KJUR.crypto.Signature({"alg": "SHA256withRSA"}); // 生成签名对象并制定为SHA256withRSA类型 sha256withrsa.init(privatekey); // 初始化privatekey sha256withrsa.updateString(keys_str); // 更新要签名的数据 const sign = pmlib.rs.hextob64(sha256withrsa.sign()); // 签名并转换为Base64字符串 Json.sign = sign; pm.request.body.raw = JSON.stringify(Json);
2.JMeter

同样在body中使用内置函数定义时间戳ts,同时添加用户定义的变量配置元件来存放私钥

导包

java

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对象

java

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

获取Json的key

java

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

对list进行排序

java

Collections.sort(keyArry);

字符串拼接

java

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首尾连接中间无换行或空格。

java

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

创建 Signature 对象并初始化

java

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

更新要签名的数据

java

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

签名

java

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

修改json对象的sign并转换为字符串写回body中

java

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

完整代码:

java

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,将其放入list for (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); // 根据给定的编码密钥创建一个新的 PKCS8EncodedKeySpec PrivateKey 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); // 修改Sign arg.setValue(dataobj.toString()); // 转换为字符串并歇回request-body中

 

总结:

感谢每一个认真阅读我文章的人!!!

作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

  1. 文档获取方式:

  2. 加入我的软件测试交流群:680748947免费获取~(同行大佬一起学术交流,每晚都有大佬直播分享技术知识点)

这份文档,对于想从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

以上均可以分享,只需要你搜索vx公众号:程序员雨果,即可免费领取

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1596929.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用深度学习集成模型进行乳腺癌组织病理学图像分类

基于预训练的VGG16和VGG19架构训练了四种不同的模型(即完全训练的 VGG16、微调的 VGG16、完全训练的 VGG19 和微调的 VGG19 模型)。最初,我们对所有单独的模型进行了5倍交叉验证操作。然后,我们采用集成策略,取预测概率…

(一)C++自制植物大战僵尸集成开发环境安装

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/uzrnw 1、下载Visual Studio集成开发环境 首先在微软官网下载Visual Studio 2022 Community版本。Community版本是免费的,并且满足个人开发的各种需求。Visual Studio 2022 下载链接:微软官网。选…

maven引入外部jar包

将jar包放入文件夹lib包中 pom文件 <dependency><groupId>com.jyx</groupId><artifactId>Spring-xxl</artifactId><version>1.0-SNAPSHOT</version><scope>system</scope><systemPath>${project.basedir}/lib/Spr…

[lesson33]C++中的字符串类

C中的字符串类 历史遗留问题 C语言不支持真正意义上的字符串C语言用字符数组和一组函数实现字符串操作C语言不支持自定义类型&#xff0c;因此无法获得字符串类型 解决方案 从C到C的进化过程引入自定义类型在C中可以通过类完成字符串类型的定义 标准库中的字符串类 C语言直…

蓝桥杯——玩具蛇

题目 小蓝有—条玩具蛇&#xff0c;一共有16节&#xff0c;上面标着数字1至16。每—节都是一个正方形的形状。相邻的两节可以成直线或者成90度角。 小蓝还有一个44的方格盒子&#xff0c;用于存放玩具蛇&#xff0c;盒子的方格上依次标着字母A到Р共16个字母。 小蓝可以折叠自…

什么是分组分析法

调查数据显示&#xff0c;2019 年年末中国大陆总人口 140005 万人。从年龄构成看&#xff0c;16 至 59 周岁年末人数为 89640 万&#xff0c;占总人口的比重为 64.0%&#xff1b;60 周岁及以上人口 25388 万人&#xff0c;占总人口的 18.1%&#xff0c;其中 65 周岁及以上人口 …

力扣LeetCode138. 复制带随机指针的链表 两种解法(C语言实现)

目录 题目链接 题目分析 题目定位&#xff1a; 解题思路 解题思路1&#xff08;粗暴但是复杂度高&#xff09; 解题思路2&#xff08;巧妙并且复杂度低&#xff09; 题目链接 138. 复制带随机指针的链表https://leetcode-cn.com/problems/copy-list-with-random-pointer/ …

OpenCV基本图像处理操作(一)——图像基本操作与形态学操作

环境配置地址 图像显示 import cv2 #opencv读取的格式是BGR import numpy as np import matplotlib.pyplot as plt#Matplotlib是RGB imgcv2.imread(cat.jpg) img_gray cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) img_gray.shape cv2.imshow("img_gray", img_gray) cv2…

【详解算法流程+程序】DBSCAN基于密度的聚类算法+源码-用K-means和DBSCAN算法对银行数据进行聚类并完成用户画像数据分析课设源码资料包

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个比较有代表性的基于密度的聚类算法。 与划分和层次聚类方法不同&#xff0c;它将簇定义为密度相连的点的最大集合&#xff0c;能够把具有足够高密度的区域划分为簇&#xff0c; 并可在噪声的空间数据…

使用webpack5+TypeScript+npm发布组件库

一、前言 作为一只前端攻城狮&#xff0c;没有一个属于自己的组件库&#xff0c;那岂不是狮子没有了牙齿&#xff0c;士兵没有了武器&#xff0c;姑娘没有了大宝SOD蜜&#xff0c;你没有了我.... 言归正传&#xff0c;下面将给大家介绍如何通过webpack5编译一个TS组件发布到NPM…

zabbix监控服务

一、监控软件的作用 作为一个运维&#xff0c;需要会使用监控系统查看服务器状态以及网站流量指标&#xff0c;利用监控系统的数据去了解上线发布的结果和网站的健康状态 利用一个优秀的监控软件&#xff0c;我们可以&#xff1a; 对系统不间断实时监控实时反馈系统当前状态保…

进程地址空间(PAS)

"进程地址空间" "虚拟地址空间" "地址空间"&#xff1b; "进程内存" ≠ "虚拟内存"&#xff1b; 32位系统虚拟地址空间为4GB&#xff0c;一般使用不完&#xff0c;用户和内核都使用不完&#xff1b; 前言&#xff1a;一个…

HarmonyOS实战开发-拼图、如何实现获取图片,以及图片裁剪分割的功能。

介绍 该示例通过ohos.multimedia.image和ohos.multimedia.mediaLibrary接口实现获取图片&#xff0c;以及图片裁剪分割的功能。 效果预览 使用说明&#xff1a; 使用预置相机拍照后启动应用&#xff0c;应用首页会读取设备内的图片文件并展示获取到的第一个图片&#xff0c;…

古月·ROS2入门21讲——学习笔记(一)核心概念部分1-14讲

讲解视频地址&#xff1a;1.ROS和ROS2是什么_哔哩哔哩_bilibili 笔记分为上篇核心概念部分和下篇常用工具部分 下篇&#xff1a;古月ROS2入门21讲——学习笔记&#xff08;二&#xff09;常用工具部分15-21讲-CSDN博客 目录 第一讲&#xff1a;ROS/ROS2是什么 1. ROS的诞生…

numpy学习笔记(5),其他实用函数

8. 更多函数 8.1 随机数 8.1.1 常用随机数 8.1.1.1 numpy.random.rand(d0, d1, …, dn) 返回[0.0, 1.0)随机浮点数&#xff0c;即大于等于0.0&#xff0c;小于1.0。d0, d1, …, dn&#xff1a;返回的数组形状 # 使用numpy.random.rand函数 import numpy as np np.random.r…

09 Php学习:超级全局变量

超级全局变量 PHP中预定义了几个超级全局变量&#xff08;superglobals&#xff09; &#xff0c;这意味着它们在一个脚本的全部作用域中都可用。 PHP 超级全局变量列表: $GLOBALS$_SERVER$_REQUEST$_POST$_GET$_FILES$_ENV$_COOKIE$_SESSION $GLOBALS $GLOBALS 是 PHP 中的…

javaee初阶———多线程(三)

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享多线程专题第三篇,关于线程安全方面的内容 如果有不足的或者错误的请您指出! 目录 八、线程安全问题(重点)1.一个典型的线程不安全的例子2.出现线程不安全的原因3.解决线程不安…

【Entity Framework】聊一聊EF中继承关系

【Entity Framework】聊一聊EF中继承关系 文章目录 【Entity Framework】聊一聊EF中继承关系一、概述二、实体类型层次结构映射三、每个层次结构一张表和鉴别器配置四、共享列五、每个类型一张表配置六、每个具体类型一张表配置七、TPC数据库架构八、总结 一、概述 Entity Fra…

高清4路HDMI编码器JR-3214HD

产品简介&#xff1a; JR-3214HD四路高清HDMI编码器是专业的高清音视频编码产品&#xff0c;该产品具有支持4路高清HDMI音视频采集功能&#xff0c;4路3.5MM独立外接音频输入&#xff0c;编码输出双码流H.264格式&#xff0c;音频MP3/AAC格式。编码码率可调&#xff0c;画面质…

盒子模型+响应式布局 + 原型链与继承

盒子模型 是什么 css布局基础,规定了元素在页面上如何呈现,以及元素之间的空间关系 由content paddingbordermargin四部分组成 为什么 盒子模型分为 标准盒子模型: 元素的宽度与高度 只包括content IE盒子模型: 元素的宽度与高度 包括content,padding,border 在实际操作中…