使用Postman和JMeter进行signature签名

news2024/11/19 23:14:56

一、前言

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

被测系统teachSignServer:

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

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

其余工具:

1.bundle.js:https://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.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中
​​​​​

​​​​​​​​​​​​​​

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

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

相关文章

对Redis锁延期的一些讨论与思考

上一篇文章提到使用针对不同的业务场景如何合理使用Redis分布式锁,并引入了一个新的问题 若定义锁的过期时间是10s,此时A线程获取了锁然后执行业务代码,但是业务代码消耗时间花费了15s。这就会导致A线程还没有执行完业务代码,A线程…

C++案例->评委打分、员工分组

#include<iostream> using namespace std; #include<vector> #include<string> #include<deque> #include<algorithm> #include<ctime> /* 有5名选手&#xff1a;选手ABCDE&#xff0c;10个评委分别对每一名选手打分&#xff0c;去除最高分…

ACL权限管理

一&#xff0c;简介 ACL是Access Control List的缩写&#xff0c;即访问控制列表。可以通过下列的实例来理解ACL的作用&#xff1a; 二&#xff0c;操作步骤 1. 添加测试目录&#xff0c;用户&#xff0c;组&#xff0c;并将用户添加到组&#xff08;创建zs,ls添加到tgroup组中…

STM32F103x 的时钟源

AHB (Advanced High-performance Bus) 高速总线&#xff0c;用来接高速外设的。 APB (Advanced Peripheral Bus) 低速总线&#xff0c;用来接低速外设的&#xff0c;包含APB1 和 APB2。 APB1&#xff1a;上面连接的是低速外设&#xff0c;包括电源接口、备份接口、 CAN 、 US…

爬取m3u8视频

网址&#xff1a;https://www.bhlsm.com/cupfoxplay/609-3-1/ 相关代码&#xff1a; #采集网址&#xff1a;https://www.bhlsm.com/cupfoxplay/609-3-1/ #正常视频网站&#xff1a;完整视频内容 # pip install pycryptodomex #流媒体文件&#xff1a;M3U8&#xff08;把完整的…

计网运输层

文章目录&#xff1a; 文章目录 概述运输层端口号、复用与分用UDP与TCP对比UDPTCP流量控制拥塞控制拥塞控制算法慢开始(slow-start)拥塞避免(congestion avoidance)快重传(fast retransmit)快恢复(fast recovery) 超时重传时间选择可靠传输实现运输连接管理建立连接连接释放 首…

台式电脑黑屏无法开机怎么办 电脑开机黑屏的解决方法

经常有朋友电脑一开机&#xff0c;发现电脑黑屏没法用了。很多人看到黑屏就懵了&#xff0c;以为电脑要报废了&#xff0c;这是什么原因?电脑开机黑屏怎么解决?一般常说的黑屏故障分为两种&#xff0c;显示屏没有任何显示以及显示英文。下面小编要为大家带来的是台式电脑黑屏…

HTMLElement.click()的回调触发踩坑

先看看以下代码 const el document.getElementById("btn") el.addEventListener("click", () > {Promise.resolve().then(() > console.log("microtask 1"));console.log("1"); }); el.addEventListener("click", (…

基于机器学习、遥感和Penman-Monteith方程的农田蒸散发混合模型研究_刘燕_2022

基于机器学习、遥感和Penman-Monteith方程的农田蒸散发混合模型研究_刘燕_2022 摘要关键词 1 绪论2 数据与方法2.1 数据2.2 机器学习算法2.3 Penman-Monteith方程2.4 Medlyn公式2.5 模型性能评估 3 基于机器学习算法的混合模型估算农田蒸散量的评价与比较4 利用人工神经网络算法…

信息矩阵、hessian矩阵与协方差矩阵

文章目录 协方差矩阵联合概率密度hessian矩阵marginalize 本节探讨信息矩阵、hessian矩阵与协方差矩阵的关系&#xff0c;阐明边缘化的原理。 一个简单的示例&#xff0c;如下&#xff1a; 来自 David Mackay. “The humble Gaussian distribution”. In: (2006). 以及手写vio第…

CSS基础(下)

一 CSS样式重置 【面试题】&#xff1a;你知道浏览器的兼容性问题有哪些&#xff1f;你进行过样式重置吗&#xff1f;进行过样式标准化吗&#xff1f; 样式重置reset/样式标准化normalize /*******第1步:样式重置(标准化):将浏览器提供的默认样式统一化 实用化***…

UnityWebGL 设置全屏

这是Unity导出Web默认打开的页面尺寸 修改后效果 修改 index.html 文件 1.div元素的id属性值为"unity-container"&#xff0c;宽度和高度都设置为100%&#xff0c;意味着该div元素将占据整个父容器的空间。canvas元素的id属性值为"unity-canvas"&#xff…

CAN转WIFI

一、 产品概述 SG-CAN-WIFI 是专为 CAN 总线网络与无线 IP 网络&#xff08;WLAN 或 Wi-Fi&#xff09;之 间或多个 CAN 总线网络之间通过无线 IP 网络&#xff08;WLAN 或 Wi-Fi&#xff09;传输 CAN 总 线数据而设计&#xff0c;无线 IP 网络&#xff08;WLAN 或 Wi-Fi&…

计算机网络面经-从浏览器地址栏输入 url 到显示主页的过程?

大概的过程比较简单&#xff0c;但是有很多点可以细挖&#xff1a;DNS解析、TCP三次握手、HTTP报文格式、TCP四次挥手等等。 DNS 解析&#xff1a;将域名解析成对应的 IP 地址。TCP连接&#xff1a;与服务器通过三次握手&#xff0c;建立 TCP 连接向服务器发送 HTTP 请求服务器…

JavaSE-05笔记【面向对象02】

文章目录 1. 类之间的关系2. is-a、is-like-a、has-a2.1 is-a2.2 is-like-a2.3 has-a 3. Object类3.1 toString()3.2 finalize()&#xff08;了解即可&#xff09;3.3 与 equals 方法 4. package 和 import4.1 package4.2 import4.3 JDK 常用开发包 5. 访问权限控制5.1 privat…

CLion的bundled MinGW能用在VSCode上吗?

跟着前辈做一个项目&#xff0c;用的极海的MCU&#xff0c;主要用到SPI和USB功能。 官方提供的SDK中的例程有 Eclipse/ Keil/ IAR 版本。 前辈根据Eclipse版本的工程信息文件&#xff08;.project 和.cproject&#xff09; 看里面链接到了哪些文件&#xff0c;然后自己手动写…

Ansible service 模块 该模块用于服务程序的管理

目录 参数将服务设置为 自启动检查端口关闭服务再次查看端口 参数 arguments #命令行提供额外的参数 enabled #设置开机启动。 name #服务名称 runlevel #开机启动的级别&#xff0c;一般不用指定。 sleep #在重启服务的过程中&#xff0c;是否等待。如在服务关闭以后等待2秒再…

gnss尾矿库安全监测系统是什么

【TH-WY1】GNSS尾矿库安全监测系统是一种利用全球导航卫星系统&#xff08;GNSS&#xff09;技术对尾矿库进行安全监测的系统。尾矿库是矿山企业的重要设施之一&#xff0c;用于存放矿山开采过程中产生的尾矿。由于尾矿库具有高能势和复杂的地质环境&#xff0c;存在溃坝、滑坡…

Python:函数

目录 前言&#xff1a; 一、函数的定义 二、函数的调用 三、函数的分类 四、全局变量和局部变量 五、函数的参数 5.1 位置参数 5.2 默认值参数 5.3 可变参数 5.4 关键字参数 5.5 命名关键字参数 5.6 参数的组合 六、函数的递归 前言&#xff1a; 函数就是一个过程…

nginx+keepalived实现nginx高可用集群以及nginx实现Gateway网关服务集群

一、前言 1、简介 Nginx作为一款高性能的Web服务器和反向代理服务器&#xff0c;被广泛使用。且现如今很多高并发场景需要后端服务集群部署&#xff0c;因此nginx也需要支持集群部署从而避免单点故障的问题。 本文将详细介绍使用 KeepalivedNginx 来实现Nginx的高可用集群和N…