当前位置: 首页 > news >正文

Gateway网关参数进行验签POST 包含requestbody 请求体封装

Gateway网关自定义拦截器的不可重复读取数据

特别注意一点, 因为在网关层 拿出 request 流之后,必须重写getbody()方法把所有的参数放进去,否则后面转发的请求无法接收到任何数据,

坑,巨坑,因为版本问题网上很多都不能兼容,

我的springboot环境 依赖包

	<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.8.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><!--	gateway版本号, 必须对应,此版本已经包含 web包	--><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId><version>2.1.0.RELEASE</version></dependency><!-- servlet 验证post请求需要重写request流 --><dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><version>2.5</version><scope>provided</scope></dependency>

需求描述:前端发起请求的参数携带sign=xxxx,后台验证签名是够正确

sign签名生成规则:
1.将post请求的body转成jsonstring (按照body里key的自然升序排列),

2 get请求的话, 把所有参数都进行排序,生成sign与前端传来的值进行验证对比

下面是非对称加密算法工 具

ApiAuthAES 类工具
package com.platform.platformgateway.util;import org.springframework.util.StringUtils;import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;/*** AES的加密和解密** @author wxq*/
public class ApiAuthAES {// 密钥// private static final String KEY = "c542384322662d446b2302faf2ab3737";// 算法private static final String ALGORITHMSTR = "AES/ECB/PKCS5Padding";/*** 将byte[]转为各种进制的字符串** @param bytes byte[]* @param radix 可以转换进制的范围,从Character.MIN_RADIX到Character.MAX_RADIX,超出范围后变为10进制* @return 转换后的字符串*/public static String binary(byte[] bytes, int radix) {return new BigInteger(1, bytes).toString(radix);}/*** base 64 encode** @param bytes 待编码的byte[]* @return 编码后的base 64 code*/public static String base64Encode(byte[] bytes) {Base64.Encoder encoder = Base64.getEncoder();return encoder.encodeToString(bytes);}/*** base 64 decode** @param base64Code 待解码的base 64 code* @return 解码后的byte[]* @throws Exception*/public static byte[] base64Decode(String base64Code) throws Exception {Base64.Decoder decoder = Base64.getDecoder();return StringUtils.isEmpty(base64Code) ? null : decoder.decode(base64Code);}/*** AES加密** @param content    待加密的内容* @param encryptKey 加密密钥* @return 加密后的byte[]* @throws Exception*/public static byte[] aesEncryptToBytes(String content, String encryptKey) throws Exception {KeyGenerator kgen = KeyGenerator.getInstance("AES");kgen.init(128);Cipher cipher = Cipher.getInstance(ALGORITHMSTR);cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(encryptKey.getBytes(), "AES"));return cipher.doFinal(content.getBytes("utf-8"));}/*** AES加密为base 64 code** @param content    待加密的内容* @param encryptKey 加密密钥* @return 加密后的base 64 code* @throws Exception*/public static String aesEncrypt(String content, String encryptKey) throws Exception {return base64Encode(aesEncryptToBytes(content, encryptKey));}/*** AES解密** @param encryptBytes 待解密的byte[]* @param decryptKey   解密密钥* @return 解密后的String* @throws Exception*/public static String aesDecryptByBytes(byte[] encryptBytes, String decryptKey) throws Exception {KeyGenerator kgen = KeyGenerator.getInstance("AES");kgen.init(128);Cipher cipher = Cipher.getInstance(ALGORITHMSTR);cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(decryptKey.getBytes(), "AES"));byte[] decryptBytes = cipher.doFinal(encryptBytes);return new String(decryptBytes);}/*** 将base 64 code AES解密** @param encryptStr 待解密的base 64 code* @param decryptKey 解密密钥* @return 解密后的string* @throws Exception*/public static String aesDecrypt(String encryptStr, String decryptKey) throws Exception {return StringUtils.isEmpty(encryptStr) ? null : aesDecryptByBytes(base64Decode(encryptStr), decryptKey);}public static String urlEncode(String str) {try {return URLEncoder.encode(str, "GBK");} catch (Exception e) {e.printStackTrace();}return "";}public static String urlDncode(String str) throws Exception {return URLDecoder.decode(str, "GBK");}public static Map<String, String> URLRequestParamMap(String strUrlParam) {Map<String, String> mapRequest = new HashMap<String, String>();String[] arrSplit = null;if (StringUtils.isEmpty(strUrlParam)) {return mapRequest;}arrSplit = strUrlParam.split("[&]");for (String strSplit : arrSplit) {String[] arrSplitEqual = null;arrSplitEqual = strSplit.split("[=]");//解析出键值if (arrSplitEqual.length > 1) {//正确解析mapRequest.put(arrSplitEqual[0], arrSplitEqual[1]);} else {if (arrSplitEqual[0] != "") {//只有参数没有值,不加入mapRequest.put(arrSplitEqual[0], "");}}}return mapRequest;}public static void main(String[] args) throws Exception {Long time = System.currentTimeMillis();System.out.println("signature:"+aesEncrypt(System.currentTimeMillis()+"","WYEB77T")); ;System.out.println(time); ;Map map = new HashMap();map.put("flag", "system");map.put("dateStr", "2022-09-01");map.put("time", time);System.out.println("signature:" + SignUtil.createSign(map, "WYEBHWgS"));Map map1 = new HashMap();map1.put("startPage", 0);map1.put("pageSize", 10);
//不能嵌套签名,无法解析/*      Map map2 = new HashMap();map2.put("app_id","app_id");map1.put("conditions",map2);
*/map1.put("time", time);System.out.println("signature:" + SignUtil.createSign(map1, "WYEB7qf9O1lg"));}}
RequestWrapper 处理body流对象封装,这里使用缓存流处理, 是避免body请求数据太多,导致截取失败的问题
RequestWrapper.java

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import reactor.core.publisher.Flux;import java.io.IOException;
import java.io.InputStream;
import java.nio.CharBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.atomic.AtomicReference;/*** @Classname TestReQuest* @Description TODO* @Date 2023/5/24 17:32* @Created by mingcai*/
@Slf4j
@Component
public class RequestWrapper {@Autowiredprivate ObjectMapper objectMapper;public String getRequestBodyDoctor(Flux<DataBuffer> bodys) {String resultJson = "";Flux<DataBuffer> body3 = bodys;InputStreamHolder holder = new InputStreamHolder();body3.subscribe(buffer -> holder.inputStream = buffer.asInputStream());if (null != holder.inputStream) {// 解析JSON的节点JsonNode jsonNode = null;try {jsonNode = objectMapper.readTree(holder.inputStream);} catch (IOException e) {throw new RuntimeException(e);}Assert.isTrue(jsonNode instanceof ObjectNode, "JSON格式异常");ObjectNode objectNode = (ObjectNode) jsonNode;// JSON节点最外层写入新的属性//objectNode.put("userId", "accessToken");// DataBuffer dataBuffer = dataBufferFactory.allocateBuffer();String json = objectNode.toString();log.info("最终的JSON数据为:{}", json);// this.setBodyString(json);return json;//dataBuffer.write(json.getBytes(StandardCharsets.UTF_8));//Flux.just(dataBuffer);}return resultJson;}private class InputStreamHolder {InputStream inputStream;}//也是取不到值public static String resolveBodyFromRequest(Flux<DataBuffer> body){AtomicReference<String> bodyRef = new AtomicReference<>();// 缓存读取的request body信息body.subscribe(dataBuffer -> {CharBuffer charBuffer = StandardCharsets.UTF_8.decode(dataBuffer.asByteBuffer());DataBufferUtils.release(dataBuffer);bodyRef.set(charBuffer.toString());});//获取request bodyreturn bodyRef.get();}}

重点来了 ,全局的过滤器, 我把重点画出来了, 至少坑了一天时间,各种尝试都无果,

最终就封装成下面这样, 返回参数无法是一个 

ServerHttpRequest, 导致我一直不能有效获取参数,每次读取之后,无法重新放入流中

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.platform.platformgateway.config.db.ConfigBean;
import com.platform.platformgateway.config.redis.BaseRedisCache;
import com.platform.platformgateway.constant.CommonConstants;
import com.platform.platformgateway.util.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.*;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;import javax.annotation.Resource;
import java.net.URI;
import java.util.*;/*** @author mingcai*/@Slf4j
@Configuration
public class AccessGatewayFilter implements GlobalFilter, Ordered {@Resourceprivate BaseRedisCache redisCache;@Resourceprivate ConfigBean configBean;@Resourceprivate ThreadPoolTaskExecutor asyncExecutor;@ResourceRequestWrapper requestWrapper;@Overridepublic Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {ServerHttpRequest originalRequest = exchange.getRequest();String token = originalRequest.getHeaders().getFirst("token");String appId = originalRequest.getHeaders().getFirst("appId");String dateTime = "";// = originalRequest.getHeaders().getFirst("time");String signature = originalRequest.getHeaders().getFirst("signature");ServerHttpResponse response = exchange.getResponse();URI originalRequestUrl = originalRequest.getURI();String path = originalRequest.getPath().toString();log.info("请求路径:" + originalRequestUrl + " service Path:" + path + " 访问IP: " + originalRequestUrl.getHost());if (StringUtils.isNotBlank(path) && path.toLowerCase().contains(CommonConstants.V2_PATH)) {token = redisCache.get(CommonConstants.V2_TOKEN_CACHE);}if (StringUtils.isNotBlank(path) && path.toLowerCase().contains(CommonConstants.V3_PATH)) {token = redisCache.get(CommonConstants.V3_TOKEN_CACHE);}// 特殊处理/*     if (path.contains(CommonConstants.V2_FIND_STUDENT_URL)) {ServerWebExchange build = getServerWebExchange(exchange, token);return chain.filter(build);}*/if (StringUtils.isBlank(signature)) {response.setStatusCode(HttpStatus.UNAUTHORIZED);log.debug("signature: 为空! ");return returnJson(response, "用户的signature参数不能为空!");}String secret = (String) redisCache.get_obj(CommonConstants.PLATFORM_CACHE + appId);if (StringUtils.isBlank(secret)) {return returnJson(response, "用户的appId错误,请核验后重试");}String method = String.valueOf(originalRequest.getMethod());String contentType = originalRequest.getHeaders().getFirst("Content-Type");try {if ("GET".equals(method)) {dateTime = originalRequest.getQueryParams().getFirst("time");if (!doGet(originalRequest, secret, signature)) {return returnJson(response, "get请求参数验证失败");}} else if ("POST".equals(method) && !Objects.requireNonNull(contentType).startsWith("multipart/form-data")) {//当body中没有缓存时,只会执行这一个拦截器, 原因是fileMap中的代码没有执行,所以需要在波多野为空时构建一个空的缓存DefaultDataBufferFactory defaultDataBufferFactory = new DefaultDataBufferFactory();DefaultDataBuffer defaultDataBuffer = defaultDataBufferFactory.allocateBuffer(0);// mediaTypeFlux<DataBuffer> bodyDataBuffer = exchange.getRequest().getBody().defaultIfEmpty(defaultDataBuffer);String finalToken = token;return DataBufferUtils.join(bodyDataBuffer).flatMap(dataBuffer -> {DataBufferUtils.retain(dataBuffer);Flux<DataBuffer> cachedFlux = Flux.defer(() -> Flux.just(dataBuffer.slice(0, dataBuffer.readableByteCount())));ServerHttpRequest mutatedRequest = new ServerHttpRequestDecorator(exchange.getRequest()) {@Overridepublic Flux<DataBuffer> getBody() {return cachedFlux;}};String json = requestWrapper.getRequestBodyDoctor(cachedFlux);JSONObject jb = JSONObject.parseObject(json);if (null == jb) {return returnJson(response, "post请求参数为空");}String time = String.valueOf(jb.getOrDefault("time", ""));log.info("str: " + time);if (!doPost(jb, secret, signature)) {return returnJson(response, "post请求参数验证失败");}Mono<Void> verifyMono = verifyUser(exchange, exchange.getResponse(), exchange.getRequest(), time);if (null != verifyMono) {return verifyMono;}ServerWebExchange newexchange = exchange.mutate().request(mutatedRequest).build();return chain.filter(getServerWebExchange(newexchange, finalToken, null));});} else {return returnJson(response, "不支持的请求方式,仅支持(GET,POST)");}} catch (Exception e) {//  throw new RuntimeException(e);log.error(" 签名错误! {}", e.getMessage());return returnJson(response, method + " 签名错误");}Mono<Void> verifyMono = verifyUser(exchange, exchange.getResponse(), exchange.getRequest(), dateTime);if (null != verifyMono) {return verifyMono;}ServerWebExchange build = getServerWebExchange(exchange, token, originalRequest);return chain.filter(build);}private static ServerWebExchange getServerWebExchange(ServerWebExchange exchange, String token,ServerHttpRequest serverHttpRequest) {ServerHttpRequest host = exchange.getRequest().mutate().header("Authorization", token).header("userTokenHead",token).header("businessId", "10000000100001").header("serviceGroup", "sky").build();return exchange.mutate().request(host).build();}@Overridepublic int getOrder() {return -200;}private String authorUser(ServerHttpRequest originalRequest) {String appId = originalRequest.getHeaders().getFirst("appId");String path = originalRequest.getPath().toString();if (StringUtils.isBlank(appId)) {return "用户的appId和secret不能为空";}String secret = (String) redisCache.get_obj(CommonConstants.PLATFORM_CACHE + appId);if (StringUtils.isBlank(secret)) {return "用户的appId错误,请核验后重试";}// 用户权限接口if (null == redisCache.get_obj(CommonConstants.PLATFORM_CACHE + appId + path)) {return "用户" + appId + "无此接口权限";}return "";}/*** GET请求*/public Boolean doGet(ServerHttpRequest request,String secret,String sign) throws Exception {//从request获取到所有的参数及其值String queryParams = request.getQueryParams().toString();if (queryParams == null) {return false;}MultiValueMap<String, String> pNames =  request.getQueryParams();Map<String, Object> map = new HashMap<>();for (String entry:pNames.keySet()){Object pValue = request.getQueryParams().getFirst(entry);map.put(entry, pValue);}String newSign = SignUtil.createSign(map,secret);if (!newSign.equals(sign)) {//returnJson(response, "get: signature 签名错误");return false;}return true;}public Boolean doPost(JSONObject json, String secret, String sign) {try {// String sign = (String) json.get("signature");String newSign = SignUtil.createSign(json, secret);if (!Objects.equals(newSign, sign)) {return false;}} catch (Exception e) {//throw new RuntimeException(e);log.error("参数请求错误: {}", e.getMessage());}return true;}public Mono<Void> verifyUser(ServerWebExchange exchange,ServerHttpResponse response,ServerHttpRequest serverHttpRequest,String dateTime) {String path = serverHttpRequest.getPath().toString();String appId = serverHttpRequest.getHeaders().getFirst("appId");if (StringUtils.isBlank(dateTime)) {response.setStatusCode(HttpStatus.UNAUTHORIZED);log.debug("用户的time时间戳参数不能为空");return returnJson(response, "用户的time时间戳参数不能为空!");}Long time = Long.valueOf(dateTime);Long nowTime = System.currentTimeMillis();//5分钟if (nowTime - time > 300000) {response.setStatusCode(HttpStatus.UNAUTHORIZED);log.debug("用户的time时间戳过期");return returnJson(response, "用户的time时间戳过期!");}// 从request对象中获取客户端ipString clientIp = Objects.requireNonNull(serverHttpRequest.getRemoteAddress()).getHostString();String authUserMsg = authorUser(serverHttpRequest);if (StringUtils.isNotBlank(authUserMsg)) {//401 网关层没有权限response.setStatusCode(HttpStatus.UNAUTHORIZED);return returnJson(response, authUserMsg);} else {//记录日志try {asyncExecutor.execute(() -> {JSONObject json = new JSONObject();Map params = serverHttpRequest.getQueryParams();json.put("path", path);json.put("clientIp", clientIp);json.put("params", params);json.put("appId", appId);OkHttpClientUtils.postJsonParams(configBean.getPlatformUrl() + "/hoe/platform/log/save", json.toString());});} catch (Exception e) {log.error("保存日志错误: {} ", e.getMessage());}}return null;}/*** 错误信息响应到客户端* @param mes response* &#064;date:  2023/5/6 14:13*/private Mono<Void> returnJson(ServerHttpResponse response, String mes) {log.info(mes);JSONObject json = new JSONObject();json.put("msg", mes);String message = JSON.toJSONString(json);response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");DataBuffer dataBuffer = response.bufferFactory().wrap(message.getBytes());return response.writeWith(Flux.just(dataBuffer));}}

对于这种封装方式,我是不太满意的,

return DataBufferUtils.join(bodyDataBuffer).flatMap(dataBuffer -> {} 这部分应该重新定义,然后统一放到后面处理的, 目前不知道怎么拆分,如果有知道的大佬,麻烦分享一下了!

下面是排序验签的工具类


import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;/*** @Classname SignUtil* @Description TODO* @Date 2023/5/23 19:00* @Created by mingcai*/
public class SignUtil {public static String createSign(Map<String, Object> originMap, String secret) throws Exception {if (originMap == null) {return null;}originMap = sortMapByKey(originMap);StringBuilder originStr = new StringBuilder();for (Map.Entry<String, Object> entry : originMap.entrySet()) {originStr.append(entry.getKey()).append("=").append(entry.getValue());originStr.append("&");}return ApiAuthAES.aesEncrypt(String.valueOf(originStr), secret);}public static Map<String, Object> sortMapByKey(Map<String, Object> map) {/*对Map对象的key升序(a->z)排列*/if (map == null || map.isEmpty()) {return null;}Map<String, Object> sortMap = new TreeMap<>(Comparator.naturalOrder());sortMap.putAll(map);return sortMap;}}

相关文章:

Gateway网关参数进行验签POST 包含requestbody 请求体封装

Gateway网关自定义拦截器的不可重复读取数据 特别注意一点, 因为在网关层 拿出 request 流之后,必须重写getbody()方法把所有的参数放进去,否则后面转发的请求无法接收到任何数据, 坑,巨坑,因为版本问题网上很多都不能兼容, 我的springboot环境 依赖包 <parent><gr…...

【Netty】字节缓冲区 ByteBuf (六)(上)

文章目录 前言一、ByteBuf类二、ByteBuffer 实现原理2.1 ByteBuffer 写入模式2.2 ByteBuffer 读取模式2.3 ByteBuffer 写入模式切换为读取模式2.4 clear() 与 compact() 方法2.5 ByteBuffer 使用案例 总结 前言 回顾Netty系列文章&#xff1a; Netty 概述&#xff08;一&…...

Python - 面向对象编程 - 实例方法、静态方法、类方法

实例方法 在类中定义的方法默认都是实例方法&#xff0c;前面几篇文章已经大量使用到实例方法 实例方法栗子 class PoloBlog:def __init__(self, name, age):print("自动调用构造方法")self.name nameself.age agedef test(self):print("一个实例方法&…...

性能测试——基本性能监控系统使用

这里写目录标题 一、基本性能监控系统组成二、环境搭建1、准备数据文件 type.db collectd.conf2、启动InfluxDB3、启动grafana4、启动collectd5、Grafana中配置数据源 一、基本性能监控系统组成 Collectd InfluxdDB Grafana Collectd 是一个守护(daemon)进程&#xff0c;用来…...

JavaCollection集合

5 Collection集合 5.1 Collection集合概述 是单列集合的顶层接口,它表示一组对象,这些对象也称Collection元素JDK不提供此接口的直接实现,它提供更具体的子接口(Set 和 List)实现package ceshi;import java.util.AbstractCollection; import java.util.ArrayList; import…...

C++中string的用法

博主简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的人。 博主主页&#xff1a;陈童学哦 所属专栏&#xff1a;CSTL 前言&#xff1a;Hello各位小伙伴们好&#xff01;欢迎来到本专栏CSTL的学习&#xff0c;本专栏旨在帮助大家了解…...

目标检测YOLO实战应用案例100讲-基于深度学习的交通场景多尺度目标检测算法研究与应用

目录 基于深度学习的交通目标检测算法研究 传统的目标检测算法 基于深度学习的目标检测算法 </...

面试:vue事件绑定修饰符

stop - 调用 event.stopPropagation()。 prevent - 调用 event.preventDefault()。 trim 自动过滤用户输入的首尾空格 number 将输出字符串转为Number类型 enter 回车键 capture - 添加事件侦听器时使用 capture 模式。 self - 只当事件是从侦听器绑定的元素本身触发时才触发…...

优思学院|从0到1,认识精益生产管理

精益生产是一种系统性的生产管理方法&#xff0c;旨在最大化价值&#xff0c;最小化浪费&#xff0c;以及提高产品质量和客户满意度。它源于丰田生产系统&#xff08;TPS&#xff09;&#xff0c;是一种基于流程优化、以人为本的管理方法&#xff0c;强调优化生产流程、减少浪费…...

HashSet创建String类型的数据

package com.test.Test07;import java.util.HashSet;public class TestString {//这是一个main方法&#xff0c;是程序的入口public static void main(String[] args) {//创建一个HashSet集合HashSet<String> hs new HashSet<>();hs.add("hello");Syste…...

真会玩:莫言用ChatGPT为余华写了一篇获奖词

5月16日&#xff0c;《收获》杂志65周年庆典暨新书发布活动在上海舞蹈中心举行。 典礼现场&#xff0c;余华凭借《文城》获得收获文学榜2021年长篇小说榜榜首。 作为老友&#xff0c;莫言在颁奖时故意卖了个关子&#xff1a;“这次获奖的是一个了不起的人物&#xff0c;当然了&…...

10 工具Bootchart的使用(windows)

Bootchart的使用方法&#xff08;windows&#xff09; 下载bootchart.jar并拷贝到windows, 然后保证windows也安装了open jdk 1.8; 下载地址&#xff1a;https://download.csdn.net/download/Johnny2004/87807973 打开设备开机启动bootchart的开关: adb shell touch /data/boo…...

电磁频谱异常监测论文阅读 | 《战场电磁环境下的电磁频谱管控指标体系研究》

文章目录 1.《战场电磁环境下的电磁频谱管控指标体系研究》1.1 电磁频谱管控的基本概念:1.2 电磁频谱管控的主要内容:1.3 指标体系1.3.1 技术指标体系1.3.2 战术指标体系1.《战场电磁环境下的电磁频谱管控指标体系研究》 1.1 电磁频谱管控的基本概念: 频谱管控是指军队领导…...

掌握好linkedin的这些技巧,你就已经超越了80%的跨境人

领英作为一款目前市面上最多人使用的在线职场社交媒体软件&#xff0c;是我们这些跨境电商人拓展客户的好渠道&#xff0c;并且很容易找到相应的外贸客户。但是领英的玩法并不像其他社交媒体平台一样简单&#xff0c;规则比较多&#xff0c;很多国内的用户都是胡乱操作&#xf…...

Linux——网络套接字1|socket编程

IP地址(公网IP),标定了主机的唯一性。 通常情况,把数据送到对方的机器是目的吗? 不是的,真正的网络通信过程其实是进程间通信,如客户端进程和服务器进程,我们把数据在主机间转发仅仅是手段,机器收到数据之后,需要将数据交付给指定的进程,当客户端有多个进程在运行时…...

stable-diffusion-webui服务器centos部署实践(成功)

之前关注stable-diffusion仅仅是因为stable-diffusion模型,但实践证明,stable-diffusion如果么有那么好的提示词功力,恐怕生成的图就是“畸形的,缺胳膊少腿的,多一块,少一块的”,如V1实践,V2实践,纸糊效果。 如果做不到其他人那样“美女自给自足”,那么我这个“大佬…...

北京筑龙作为软件服务商出席《国企阳光采购标准》研讨会

近日&#xff0c;由中国企业国有产权交易机构协会市场创新专业委员会主办、青岛产权交易所有限公司承办的《国企阳光采购标准》研讨会在青岛召开&#xff0c;该会议共有19家已开展采购业务的各地产权交易机构参加&#xff0c;北京筑龙作为软件服务商出席会议。 《国企阳光采购标…...

如何动态显示物品提示?

UE5 插件开发指南 前言0 提示信息窗口类前言 为了使物品的排列简洁,各种游戏里的物品信息都是以提示的形式展示出来,而不是整个铺排陈列,只需要玩家鼠标悬停在物品上就自动显示出提示窗口,如下图所示: 这些提示信息在物品定义数据资产中已经定义了,所以这里要做的只是将…...

推荐试试这个简单好用的手机技巧

技巧一&#xff1a;一键锁屏 除了按住手机电源键进行锁屏外&#xff0c;还有其他一些快捷方法可以实现锁屏操作。 对于苹果手机用户&#xff0c;可以按照以下步骤进行设置&#xff1a; 1.打开手机的设置应用&#xff0c;通常可以在主屏幕或应用列表中找到该图标。 2.在设置…...

传染病学模型 | Matlab实现SIS传染病学模型 (SIS Epidemic Model)

文章目录 效果一览基本描述模型介绍程序设计参考资料效果一览 基本描述 传染病学模型 | Matlab实现SIS传染病学模型 (SIS Epidemic Model) 模型介绍 SIS模型是一种基本的传染病学模型,用于描述一个人群中某种传染病的传播情况。SIS模型假设每个人都可以被感染,即没有免疫力,…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

DAY 47

三、通道注意力 3.1 通道注意力的定义 # 新增&#xff1a;通道注意力模块&#xff08;SE模块&#xff09; class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...

Go 语言接口详解

Go 语言接口详解 核心概念 接口定义 在 Go 语言中&#xff0c;接口是一种抽象类型&#xff0c;它定义了一组方法的集合&#xff1a; // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的&#xff1a; // 矩形结构体…...

oracle与MySQL数据库之间数据同步的技术要点

Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异&#xff0c;它们的数据同步要求既要保持数据的准确性和一致性&#xff0c;又要处理好性能问题。以下是一些主要的技术要点&#xff1a; 数据结构差异 数据类型差异&#xff…...

论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)

宇树机器人多姿态起立控制强化学习框架论文解析 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架&#xff08;一&#xff09; 论文解读&#xff1a;交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)

目录 一、&#x1f44b;&#x1f3fb;前言 二、&#x1f608;sinx波动的基本原理 三、&#x1f608;波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、&#x1f30a;波动优化…...