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

news2024/12/24 21:24:35

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 {

    @Autowired
    private 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 body
        return 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 {

    @Resource
    private BaseRedisCache redisCache;
    @Resource
    private ConfigBean configBean;
    @Resource
    private ThreadPoolTaskExecutor asyncExecutor;

    @Resource
    RequestWrapper requestWrapper;

    @Override
    public 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);
                // mediaType
                Flux<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()) {
                        @Override
                        public 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();
    }

    @Override
    public 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对象中获取客户端ip
        String 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;
    }

}

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

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

相关文章

Linux--ServerProgramming--TCP\IP协议族

1.TCP/IP 协议族 1.1 TCP/IP协议族及主要协议 TCP/IP 协议族是一个四层协议系统。自上而下为&#xff08;如下图所示&#xff09;&#xff1a;应用层传输层网络层数据链路层 应用层负责处理应用程序逻辑&#xff0c;在用户空间实现。&#xff08;少数服务器程序在内核中实现。…

如何下载外文期刊文献,怎么下载又快又省力!

文章开头我们先了解一下下面这些查找外文期刊文献的数据库: 1、Web of Science&#xff1a;是获取全球学术信息的重要数据库。它收录了全球13000多种权威的、高影响力的学术期刊&#xff0c;内容涵盖自然科学、工程技术、生物医学、社会科学、艺术与人文等领域。其中以SCIE、S…

OpenCV入门简单的人脸识别项目

在学会图像处理和打开摄像头获取视频流后&#xff0c;就可以开展简单的人脸识别项目。 文章目录 检测人脸区域并绘制矩形多个人脸进行识别绘制五官位置视频检测人脸人脸识别 人脸识别首先需要检测到人脸。 检测人脸区域并绘制矩形 # 加载图片img face_recognition.load_image…

接口全生命周期的生产利器 ApiKit

一、ApiKit 整体介绍&#xff1a; 1、接口管理的需求与现状&#xff1a; 在软件项目研发的过程中&#xff0c;必然存在以下几个需求&#xff1a; API 接口文档的管理&#xff0c;常用的解决方案有 Swagger API 接口的调试&#xff0c;常用的解决方案有 Postman API 接口的自…

Java——网络编程套接字

目录 一、网络编程基础 1.1 为什么需要网络编程&#xff1f;——丰富的网络资源 二、什么是网络编程? 三、网络编程中的基本概念 3.2 请求和响应 3.3 客户端和服务端 常见的客户端服务端模型 四、Socket套接字 五、通信模型 5.1 Java数据报套接字通信模型 5.2 Java流…

【大数据之Hive】二、Hive安装

Hive安装部署&#xff08;最小化部署&#xff09; 安装部署Hive&#xff08;最小化只用于本机测试环境中&#xff0c;不可用于生产环境&#xff09;&#xff0c;并运行。 步骤&#xff1a; &#xff08;1&#xff09;把apache-hive-3.1.3-bin.tar.gz解压到/opt/module/目录下&…

IIC总线学习

IIC总线 1.总线空闲状态。2.IIC总线的起始停止条件。3.IIC总线的数据传送4.IIC总线的应答5.IIC时序 1.总线空闲状态。 总线空闲时&#xff0c;SDA和SCL均为高电平。 2.IIC总线的起始停止条件。 起始条件&#xff1a;在SCL为高时&#xff0c;SDA总线被拉低&#xff0c;即出现…

json-server的基本使用

1、mock是什么&#xff1f; mockjs 作用&#xff1a;生成随机数据&#xff0c;拦截 Ajax 请求 目的&#xff1a;很多时候前端开发页面的过程中&#xff0c;后端的接口并没有写好&#xff0c;这个时候需要前端自己定义接口及接口的返回数据的结构体&#xff0c;这个时候就需要…

【活动】云计算的优势与发展趋势

写在前面 人生是一场消耗&#xff0c;要把美好的时光放在喜欢的人与事上。 一、前言 云计算作为一种新兴的信息技术应用解决方案&#xff0c;可以帮助企业解决IT资源利用率低、IT基础设施的建设和维护成本高、IT系统的安全和稳定性问题等痛点&#xff0c;提高企业的业务灵活性…

react表格行下载文件方法总结

一、前言 下载文件时&#xff0c;后台接口返回的响应体是文件流格式的&#xff0c;前端接收时如果不进行处理&#xff0c;就会无法正确下载文件&#xff08;有可能会直接打开文件等&#xff09;。 在此记录下react的表格行使用file-saver下载文件的方法。&#xff08;注意不同…

文件包含的本质、预处理符号、# vs ##

何为头文件&#xff1f; 在C语言中&#xff0c;文件包含是一种常见的编程技术&#xff0c;它允许程序员在一个源文件中使用另一个源文件中的函数或变量。 文件包含通常使用#include预处理指令来实现。#include指令告诉预处理器将文件的内容插入到当前文件的指定位置中。 例如&a…

今天面了个阿里拿 38K 出来的,让我见识到了测试界的天花板

一直觉得自己的技术已经很不错了&#xff0c;直到最近遇到了一个阿里来的大佬 5年测试&#xff0c;应该是能达到资深测试的水准&#xff0c;即不仅能熟练地开发业务&#xff0c;而且还能熟悉项目开发&#xff0c;测试&#xff0c;调试和发布的流程&#xff0c;而且还应该能全面…

第04章 IDEA的安装与使用

【Why IDEA ?】 【注】JetBrains官方说明&#xff1a; 尽管我们采取了多种措施确保受访者的代表性&#xff0c;但结果可能会略微偏向 JetBrains 产品的用户&#xff0c;因为这些用户更有可能参加调查。 此外&#xff0c;2022年&#xff0c;某美国软件开发商在对近千名专业的J…

chatgpt赋能python:Python中的Split函数:去空操作详解

Python中的Split函数&#xff1a;去空操作详解 在Python编程中&#xff0c;我们经常需要对字符串进行操作。而字符串的分割操作在其中是非常常见的操作。Python中的split函数便是用来实现字符串分割的函数。不过&#xff0c;在使用split函数时通常还需要经过去除空格等操作。 …

Inno软件打包发布并后台安装相关驱动

bat安装 bat脚本的具体书写可自行百度。 其主要思路是将Driver文件存放在Bin下面&#xff0c;先安装Bin&#xff0c;然后在执行bat脚本来安装driver 具体些步骤&#xff1a; &#xff08;1&#xff09;编写一个install.bat的脚本 echo off ::下面三行代码是不显示dos界面 i…

加急!指定日本| 教育学老师9天获邀请函申报CSC

S老师拟申报今年的国家留学基金委&#xff08;CSC&#xff09;公派访问学者项目&#xff0c;因所在高校要求提前上报&#xff0c;所以委托时只留给我们11天申请时间&#xff0c;且指定日本高校。最终我们在第9天获得熊本大学邀请函&#xff0c;提前完成了客户的委托。 S老师背景…

Android平台GB28181设备接入模块如何对接NV21、YV12、RGB、YUV等外部数据

技术背景 我们在对接Android平台GB28181设备接入模块的开发者时&#xff0c;遇到这样的场景&#xff0c;除了Android设备&#xff08;如执法记录仪、智能安全帽等&#xff09;自带的camera或camera2前后摄像头数据外&#xff0c;还有些场景是需要外部编码前或编码后数据&#…

淡季不淡,满帮一季度净利创历史新高的背后原因是什么?

进入五月&#xff0c;经济复苏的成果越发体现在很多基础行业的表现中。经济的“大动脉”货运行业&#xff0c;也迎来一份新答卷。 北京时间5月22日美股盘前&#xff0c;数字货运平台满帮集团&#xff08;NYSE:YMM&#xff0c;简称&#xff1a;满帮&#xff09;&#xff0c;发布…

预约直播领券,1%服务费,视频号618大促激励来了!

视频号直播6.18大促激励计划来了&#xff01; 激励有效期为2023年05月31日20:00:00至2023年06月18日23:59:59&#xff1b;参与对象为活动期间满足视频号开播条件的。 通过视频号直播选择“购物”类目开播开通橱窗功能的商家和达人&#xff0c;可参与4大激励计划。 预约领券激…

pix2pixHD---model---生成器

然后是model的搭建&#xff1a; 在creat_model函数中&#xff1a; import torch def create_model(opt):if opt.model pix2pixHD:from .pix2pixHD_model import Pix2PixHDModel, InferenceModelif opt.isTrain:model Pix2PixHDModel()else:model InferenceModel()else:fro…