SpringBoot请求参数加密、响应参数解密

news2024/11/23 21:08:15

SpringBoot请求参数加密、响应参数解密

1.说明

在项目开发工程中,有的项目可能对参数安全要求比较高,在整个http数据传输的过程中都需要对请求参数、响应参数进行加密,也就是说整个请求响应的过程都是加密处理的,不在浏览器上暴露请求参数、响应参数的真实数据。

补充:也可以用于单点登录,在请求参数中添加时间戳,后台解析请求参数对时间戳进行校验,比如当前时间和请求参数中的时间戳相差多少秒、分钟才能进行放行,返回token。这样做的好处在于请求端每次加密之后的密文都是变化的,也能够避免携带相同的报文可以重复的登录。

2.准备工作

1.引入依赖, 创建SpringBoot工程

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		<dependency>
			<groupId>cn.hutool</groupId>
			<artifactId>hutool-all</artifactId>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io</artifactId>
			<version>2.11.0</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
		</dependency>
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

		<!--swagger-->
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger2</artifactId>
			<version>3.0.0</version>
		</dependency>
		<dependency>
			<groupId>io.springfox</groupId>
			<artifactId>springfox-swagger-ui</artifactId>
			<version>3.0.0</version>
		</dependency>
		<dependency>
			<groupId>io.swagger</groupId>
			<artifactId>swagger-annotations</artifactId>
			<version>1.5.22</version>
		</dependency>
		<dependency>
			<groupId>com.github.xiaoymin</groupId>
			<artifactId>swagger-bootstrap-ui</artifactId>
			<version>1.8.7</version>
		</dependency>
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-lang3</artifactId>
		</dependency>
	</dependencies>

3.代码实现

1.定义两个注解

/**
 * @description: : 请求参数解密
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DecryptionAnnotation {
}

/**
 * @description: 响应参数加密
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface EncryptionAnnotation {
}

2.加密解密实现核心代码

DecryptRequestBodyAdvice:请求参数解密,针对post请求

package com.llp.crypto.advice;

import cn.hutool.json.JSONUtil;
import com.llp.crypto.annotation.DecryptionAnnotation;
import com.llp.crypto.utils.AESUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;

/**
 * @description: 请求参数解密,针对post请求
 */

@Slf4j
@ControllerAdvice
public class DecryptRequestBodyAdvice implements RequestBodyAdvice {

    /**
     * 方法上有DecryptionAnnotation注解的,进入此拦截器
     *
     * @param methodParameter 方法参数对象
     * @param targetType      参数的类型
     * @param converterType   消息转换器
     * @return true,进入,false,跳过
     */
    @Override
    public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return methodParameter.hasMethodAnnotation(DecryptionAnnotation.class);
    }

    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
        try {
            return new MyHttpInputMessage(inputMessage, parameter);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 转换之后,执行此方法,解密,赋值
     *
     * @param body          spring解析完的参数
     * @param inputMessage  输入参数
     * @param parameter     参数对象
     * @param targetType    参数类型
     * @param converterType 消息转换类型
     * @return 真实的参数
     */
    @SneakyThrows
    @Override
    public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        log.info("解密后的请求报文:{}", body);
        return body;
    }


    /**
     * 如果body为空,转为空对象
     *
     * @param body          spring解析完的参数
     * @param inputMessage  输入参数
     * @param parameter     参数对象
     * @param targetType    参数类型
     * @param converterType 消息转换类型
     * @return 真实的参数
     */
    @Override
    public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
        return body;
    }

    class MyHttpInputMessage implements HttpInputMessage {
        private HttpHeaders headers;

        private InputStream body;

        private MethodParameter parameter;

        public MyHttpInputMessage(HttpInputMessage inputMessage, MethodParameter parameter) throws Exception {
            this.headers = inputMessage.getHeaders();
            //只对post请求进行加密
            if (parameter.hasMethodAnnotation(PostMapping.class)) {
                /*
                 *请求报文示例:
                 *  {
                 *  "requestData":"JF7kvl9Wd/vgdmAS8JijsQ=="
                 *  }
                 */
                String decrypt = AESUtil.decrypt(easpData(IOUtils.toString(inputMessage.getBody(), "UTF-8")));
                log.info("解密后的请求参数:{}", decrypt);
                this.body = IOUtils.toInputStream(decrypt, "UTF-8");
            } else {
                this.body = inputMessage.getBody();
            }
        }

        @Override
        public InputStream getBody() throws IOException {
            return body;
        }

        @Override
        public HttpHeaders getHeaders() {
            return headers;
        }

    }

    public String easpData(String requestData) {
        if (requestData != null && !requestData.equals("")) {
            String start = "requestData";
            if (requestData.contains(start)) {
                return JSONUtil.parseObj(requestData).getStr(start);
            } else {
                throw new RuntimeException("参数【requestData】缺失异常!");
            }
        }
        return "";
    }
}

GetDeleteDecryptAspect:针对get、delete请求参数进行解密

@Aspect
//值越小优先级越高
@Order(-1)
@Component
@Slf4j
public class GetDeleteDecryptAspect {

    /**
     * 对get、delete方法进行解密
     * @param point
     * @return
     * @throws Throwable
     */
    @Around("@annotation(com.llp.crypto.annotation.DecryptionAnnotation) && " + "(@annotation(org.springframework.web.bind.annotation.GetMapping) || @annotation(org.springframework.web.bind.annotation.DeleteMapping))")
    public Object aroundMethod(ProceedingJoinPoint point) throws Throwable {
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        // 获取到请求的参数列表
        Object[] args = point.getArgs();
        // 判断方法请求参数是否需要解密
        if (method.isAnnotationPresent(DecryptionAnnotation.class)) {
            try {
                this.decrypt(args, point);
                log.info("返回解密结果=" + args);
            } catch (Exception e) {
                e.printStackTrace();
                log.error("对方法method :【" + method.getName() + "】入参数据进行解密出现异常:" + e.getMessage());
            }
        }
        // 执行将解密的结果交给控制器进行处理,并返回处理结果
        return point.proceed(args);
    }

    /**
     * 前端对请求参数进行加密,最终将这个加密的字符串已 localhost:48080?data=xxx这样的方式进行传递
     * 后端后去到 data的数据进行解密最终得到解密后的数据
     * @param args
     * @param point
     * @throws Exception
     */
    // 解密方法
    @SuppressWarnings("unchecked")
    public void decrypt(Object[] args, ProceedingJoinPoint point) throws Exception {
        ServletRequestAttributes sc = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = sc.getRequest();
        String data = request.getParameter("data");
        log.info("data: " + data);
        // 将密文解密为JSON字符串
        Class<?> aClass = args[0].getClass();
        log.info("数据类型:{}",aClass.getClass());
        if (StringUtils.isNotEmpty(data)) {
            // 将JSON字符串转换为Map集合,并替换原本的参数
            args[0] = JSONUtil.toBean(AESUtil.decrypt(data), args[0].getClass());
        }
    }
}

EncryptResponseBodyAdvice:响应参数解密,针对统一返回结果类的装配

/**
 * @description: 响应加密
 */

@Slf4j
@ControllerAdvice
public class EncryptResponseBodyAdvice implements ResponseBodyAdvice {


    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return methodParameter.hasMethodAnnotation(EncryptionAnnotation.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        log.info("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行加密");
        // 只针对回参类型为CommonResult的对象,进行加密
        if (body instanceof CommonResult) {
            CommonResult commonResult = (CommonResult) body;
            Object data = commonResult.getData();
            if (Objects.nonNull(data)) {
                // 将响应结果转换为json格式
                String result = JSONUtil.toJsonStr(data);
                log.info("返回结果:{}", result);
                try {
                    String encrypt = AESUtil.encrypt(result);
                    commonResult.setData(encrypt);
                    log.info("返回结果加密=" + commonResult);
                } catch (Exception e) {
                    log.error("对方法method :【" + methodParameter.getMethod().getName() + "】返回数据进行解密出现异常:" + e.getMessage());
                }
                return commonResult;
            }
        }
        return body;
    }

}

3.统一返回结果

@Data
public class CommonResult<T> {
    private String code;
    private String msg;
    private T data;

    public CommonResult() {
    }

    public CommonResult(T data) {
        this.data = data;
    }

    /**
     * 表示成功的Result,不携带返回数据
     *
     * @return
     */
    public static CommonResult success() {
        CommonResult result = new CommonResult();
        result.setCode("200");
        result.setMsg("success");
        return result;
    }

    /**
     * 便是成功的Result,携带返回数据
     * 如果需要在static方法使用泛型,需要在static后指定泛型表示 static<T>
     *
     * @param data
     * @return
     */
    public static <T> CommonResult<T> success(T data) {
        CommonResult<T> result = new CommonResult<>(data);
        result.setCode("200");
        result.setMsg("success");
        return result;
    }

    /**
     * 失败不携带数据
     * 将错误的code、msg作为形参,灵活传入
     *
     * @param code
     * @param msg
     * @return
     */
    public static CommonResult error(String code, String msg) {
        CommonResult result = new CommonResult();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

    /**
     * 失败携带数据
     * 将错误的code、msg、data作为形参,灵活传入
     * @param code
     * @param msg
     * @param data
     * @param <T>
     * @return
     */
    public static <T> CommonResult<T> error(String code, String msg, T data) {
        CommonResult<T> result = new CommonResult<>(data);
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }

}

4.加密工具类

public class AESUtil {
    // 加解密方式
    private static final String AES_ALGORITHM = "AES/ECB/PKCS5Padding";

    // 与前端统一好KEY
    private static final String KEY = "abcdsxyzhkj12345";

    // 获取 cipher
    private static Cipher getCipher(byte[] key, int model) throws Exception {
        SecretKeySpec secretKeySpec = new SecretKeySpec(KEY.getBytes(), "AES");
        Cipher cipher = Cipher.getInstance(AES_ALGORITHM);
        cipher.init(model, secretKeySpec);
        return cipher;
    }

    // AES加密
    public static String encrypt(String data) throws Exception {
        Cipher cipher = getCipher(KEY.getBytes(), Cipher.ENCRYPT_MODE);
        return Base64.getEncoder().encodeToString(cipher.doFinal(data.getBytes("UTF-8")));
    }

    // AES解密
    public static String decrypt(String data) throws Exception {
        Cipher cipher = getCipher(KEY.getBytes(), Cipher.DECRYPT_MODE);
        return new String(cipher.doFinal(Base64.getDecoder().decode(data.getBytes("UTF-8"))),"UTF-8");
    }

    public static byte[] decryptUrl(String url) throws Exception {
        Cipher cipher = getCipher(KEY.getBytes(), Cipher.DECRYPT_MODE);
        return cipher.doFinal(Base64.getDecoder().decode(url.replaceAll(" +", "+")));
    }

    // AES解密MySQL AES_ENCRYPT函数加密密文
    public static String aesDecryptMySQL(String key, String content){
        try {
            SecretKey secretKey = generateMySQLAESKey(key,"ASCII");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            byte[] cleartext = Hex.decodeHex(content.toCharArray());
            byte[] ciphertextBytes = cipher.doFinal(cleartext);
            return new String(ciphertextBytes, StandardCharsets.UTF_8);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    //加密
    public static String aesEncryptMySQL(String key2, String content) {
        try {
            SecretKey key = generateMySQLAESKey(key2,"ASCII");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.ENCRYPT_MODE, key);
            byte[] cleartext = content.getBytes("UTF-8");
            byte[] ciphertextBytes = cipher.doFinal(cleartext);
            return new String(Hex.encodeHex(ciphertextBytes));

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static SecretKeySpec generateMySQLAESKey(final String key, final String encoding) {
        try {
            final byte[] finalKey = new byte[16];
            int i = 0;
            for(byte b : key.getBytes(encoding)) {
                finalKey[i++%16] ^= b;
            }
            return new SecretKeySpec(finalKey, "AES");
        } catch(UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }
    @Test
    public void decodeTest() {
        try {
            String a = "{\"username\":\"admin\",\"deptId\":\"1250500000\",\"userId\":\"1\",\"phone\":\"15195928695\"}";
            String encrypt = AESUtil.encrypt(a);
            System.out.println("加密后的字符串: "+encrypt);
            System.out.println("解密后的字符串:" +AESUtil.decrypt(encrypt));

            String str = "5tAayXF5ZcPC9yoNvBIT0fw2Li2uoxUhGyMq4JKUvCttOFnU7iKovyB9pm/ZV+2qU8h2htdk5s6ht9kCpTGG9WZAGTdMUgIJkD/Tf6IQ3gw=";

            String decrypt = AESUtil.decrypt(IOUtils.toString(str.getBytes(), "UTF-8"));
            System.out.println(decrypt);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5.请求流支持多次获取

/**
 * 请求流支持多次获取
 */
public class InputStreamHttpServletRequestWrapper extends HttpServletRequestWrapper {

    /**
     * 用于缓存输入流
     */
    private ByteArrayOutputStream cachedBytes;

    public InputStreamHttpServletRequestWrapper(HttpServletRequest request) {
        super(request);
    }

    @Override
    public ServletInputStream getInputStream() throws IOException {
        if (cachedBytes == null) {
            // 首次获取流时,将流放入 缓存输入流 中
            cacheInputStream();
        }

        // 从 缓存输入流 中获取流并返回
        return new CachedServletInputStream(cachedBytes.toByteArray());
    }

    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(getInputStream()));
    }

    /**
     * 首次获取流时,将流放入 缓存输入流 中
     */
    private void cacheInputStream() throws IOException {
        // 缓存输入流以便多次读取。为了方便, 我使用 org.apache.commons IOUtils
        cachedBytes = new ByteArrayOutputStream();
        IOUtils.copy(super.getInputStream(), cachedBytes);
    }

    /**
     * 读取缓存的请求正文的输入流
     * <p>
     * 用于根据 缓存输入流 创建一个可返回的
     */
    public static class CachedServletInputStream extends ServletInputStream {

        private final ByteArrayInputStream input;

        public CachedServletInputStream(byte[] buf) {
            // 从缓存的请求正文创建一个新的输入流
            input = new ByteArrayInputStream(buf);
        }

        @Override
        public boolean isFinished() {
            return false;
        }

        @Override
        public boolean isReady() {
            return false;
        }

        @Override
        public void setReadListener(ReadListener listener) {

        }

        @Override
        public int read() throws IOException {
            return input.read();
        }
    }

}

4.测试

1.测试类

@Slf4j
@RestController
@Api(tags = "测试加密解密")
public class TestController {

    /**
     * 请求示例:
     * {
     * "requestData":"5tAayXF5ZcPC9yoNvBIT0fw2Li2uoxUhGyMq4JKUvCttOFnU7iKovyB9pm/ZV+2qU8h2htdk5s6ht9kCpTGG9WZAGTdMUgIJkD/Tf6IQ3gw="
     * }
     *
     * @return
     */
    @PostMapping(value = "/postEncrypt")
    @ApiOperation("测试post加密")
    @EncryptionAnnotation
    @DecryptionAnnotation
    public CommonResult<String> postEncrypt(@RequestBody UserReqVO userReqVO) {
        System.out.println("userReqVO: ============>" + userReqVO);
        return CommonResult.success("成功");
    }


    @GetMapping(value = "/getEncrypt")
    @ApiOperation("测试get加密")
    @DecryptionAnnotation // requestBody 自动解密
    public CommonResult<UserReqVO> getEncrypt(String data) {
        log.info("解密后的数据:{}",data);
        UserReqVO userReqVO = JSONUtil.toBean(data, UserReqVO.class);
        //UserReqVO(username=admin, deptId=1250500000, userId=1, phone=15195928695)
        log.info("用户信息:{}",userReqVO);
        return CommonResult.success(userReqVO);
    }
}
@ApiModel(description = "用户请求vo")
@Data
public class UserReqVO {

    @ApiModelProperty(value = "用户名", required = true)
    private String username;

    @ApiModelProperty(value = "部门id",required = true)
    private Long deptId;

    @ApiModelProperty(value = "用户id",required = true)
    private Long userId;

    @ApiModelProperty(value = "电话号码",required = true)
    private String phone;

}

测试结果

image-20240108110033110

image-20240108110124687

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

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

相关文章

[linux]编译一个ko文件并运行

一、需求 有一段代码需要在运行时加载注入内核中&#xff0c;当用户层需要访问时可以提供内核态环境去运行。 二、c代码构建 // #include <errno.h> // #include <string.h> // #include <stdio.h> // #include <fcntl.h> // #include <stdlib.h…

全新小白菜QQ云端机器人登录系统源码 /去除解密授权学习版源码

源码介绍&#xff1a; 全新小白菜QQ云端机器人登录系统源码&#xff0c;是一款经过全面解密的授权学习版源码。 这款源码已解除了授权版的限制&#xff0c;然而许多人可能对其用途并不了解。实际上&#xff0c;该源码主要面向群机器人爱好者设计。它是一个基于挂机宝机器人框…

基于3D Gaussian Splatting与NeRF实现三维重建(使用IPhone创建数据集)

基于Spectacular AI与NeRF实现三维重建-使用IPhone创建数据集 前言项目简介创建数据集扫描处理数据集 解析数据集Python环境 Windows ffmpeg 环境搭建数据集处理安装Nerfstudio需要CUDA环境 依次安装依赖pip install nerfstudio Nerfstudio实现效果开始训练参数配置实时训练浏览…

吐血整理,自动化测试框架总结,一篇从0到1策底打通...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 1、什么是框架 定…

目标检测开源数据集——道路坑洼

一、危害 对车辆的影响&#xff1a;道路坑洼会导致车辆行驶不稳&#xff0c;增加车辆的颠簸&#xff0c;不仅影响乘坐舒适度&#xff0c;还可能对车辆的悬挂系统、轮胎等造成损害。长期在坑洼路面上行驶&#xff0c;车辆的减震系统、悬挂系统等关键部件容易受损&#xff0c;进…

(超详细)2-YOLOV5改进-添加SimAM注意力机制

1、在yolov5/models下面新建一个SimAM.py文件&#xff0c;在里面放入下面的代码 代码如下&#xff1a; import torch import torch.nn as nnclass SimAM(torch.nn.Module):def __init__(self, e_lambda1e-4):super(SimAM, self).__init__()self.activaton nn.Sigmoid()self…

中国电子学会2022年12月份青少年软件编程Scratch图形化等级考试试卷二级真题(含答案)

1.运行下列哪段程序&#xff0c;可以让狗狗走到木屋门口&#xff1f;&#xff08;C&#xff09;(2分) A. B. C. D. 答案解析&#xff1a;小窝在狗狗前方90度方向&#xff0c;我们使用程序让狗狗向前移动即可&#xff0c;移动距离不会超过舞台的宽度&#xff08;480&#xf…

git init 执行后发生了什么?

首先在磁盘中创建一个新目录 Git&#xff0c;进入该目录后执行 git init 初始化。这个时候目录下会创建一个隐藏目录 ./git&#xff0c;这个./git 目录叫做 Git 版本库或者仓库 $ git init Initialized empty Git repository in D:/Git/.git/在讲解.git 目录内容前&#xff0c…

Xcode15 升级问题记录

这里写自定义目录标题 新版本Xcode15升级问题1&#xff1a;rsync error: some files could not be transferred (code 23) at ...参考 新版本Xcode15升级 下载地址&#xff1a;https://developer.apple.com/download/all/ 我目前使用的版本是Xcode15.2 我新创建了一个项目&…

《JVM由浅入深学习【七】 2024-01-11》JVM由简入深学习提升分享

亲爱的读者们&#xff0c;欢迎来到本篇博客&#xff0c;这是JVM第七次分享&#xff0c;下面是七个JVM常用常面的分享&#xff0c;请笑纳 目录 1. 几个与JVM 内存相关的核心参数2.如何计算一个对象的大小3.堆为什么要分为新生代和老年代4.JVM堆的年轻代为什么要有两个 Survivor…

分布式限流和本地限流那些事?

分布式限流和本地限流的目的是一样的&#xff0c;当然我建议技术人在自己的服务中优先考虑本地限流&#xff0c;那样对于自己的API的影响会小一点。 限流这种技术&#xff0c;在没有触发限流的阈值的时候&#xff0c;是不会有什么大的问题的&#xff0c;当时一旦触发阈值&…

全网独家:基于openEuler-22.03-LTS-SP4底包构建opengauss 2.1.0 rpm包安装的单机轻量版数据库容器

本文基于openEuler-22.03-LTS-SP4底包,使用rpm安装方式构建opengauss 2.1.0 单机轻量版数据库容器。 一、openEuler-22.03-LTS-SP4底包 1、下载镜像 下载链接 sha256:ffe6c97428d6e684519dd670c18d6681861bdc17719b0387c6e11a65e37a771f openEuler-docker.x86_64.tar.xz …

景联文科技:以高质量数据赋能文生图大模型

1月5日&#xff0c;在智求共赢・中国AIGC产业应用峰会暨无界AI生态合作伙伴大会上&#xff0c;中国AIGC产业联盟联合无界AI发布了《中国AIGC文生图产业白皮书2023》&#xff0c;从AIGC文生图发展历程、主流工具、产业实践以及规模预测等多个维度&#xff0c;全面揭示了中国AIGC…

electron+vue编辑Office Word?

Electron 桌面应用是基于 Chromium 内核的&#xff0c;而谷歌Chrome取消了对PPAPI插件支持后&#xff0c;在线Office方案纷纷失效&#xff0c;只能更换国产浏览器在低版本浏览器苟延残喘&#xff0c;不能用于electronvue项目。 经过小编不断的寻找&#xff0c;终于找到一款至今…

vue3的福音框架arco.design

前言&#xff1a; 在vue2于2023年底正式宣布不在维护&#xff0c;vue3使用越来越频繁的时刻&#xff0c;我们实现项目的辅助框架也越来越多。element, iview, antd 等经典框架继续风靡一时&#xff0c;不过也有很多好的框架&#xff0c;功能也强大&#xff0c;比如我们今天说的…

数据交付变革:研发到产运自助化的转型之路

作者 | Chris 导读 本文讲述为了提升产运侧数据观察、分析、决策的效率&#xff0c;支持业务的快速迭代&#xff0c;移动生态数据研发部对数仓建模与BI工具完成升级&#xff0c;采用宽表建模与TDA平台相结合的方案&#xff0c;一站式自助解决数据应用需求。在此过程中&#xff…

限流算法之计数器法

文章目录 一、计数器法是什么&#xff1f;二、模拟限流算法java版效果 一、计数器法是什么&#xff1f; 计数器法是限流算法里最简单也是最容易实现的一种算法。 比如&#xff1a;对于一个接口来说&#xff0c;我们1分钟的访问次数不能超过100个。那么我们可以这么做&#xff…

大话 JavaScript(Speaking JavaScript):第二十一章到第二十五章

第二十一章&#xff1a;数学 原文&#xff1a;21. Math 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 Math对象用作多个数学函数的命名空间。本章提供了一个概述。 数学属性 Math的属性如下&#xff1a; Math.E 欧拉常数&#xff08;e&#xff09; Math.LN2 2 …

论文阅读《Generalizing Face Forgery Detection with High-frequency Features》

高频噪声分析会过滤掉图像的颜色内容信息。 本文设计了三个模块来充分利用高频特征&#xff0c; 1.多尺度高频特征提取模块 2.双跨模态注意模块 3.残差引导空间注意模块&#xff08;也在一定程度上体现了两个模态的交互&#xff09; SRM是用于过滤图像的高频噪声 输入的图…

韩语干货-TOPIK韩语初级必备词汇|柯桥外语学校-小语种考级培训

물【名】水 좋다【形】好 나쁘다【形】坏&#xff0c;不好 맛있다【形】好吃 맛없다【形】不好吃&#xff0c;难吃 책【名】书 없다【形/动】没有/不在 있다【形/动】有&#xff0c;在 읽다【动】读 많다【形】多 적다【形】少 좋아하다【动】喜欢 싫다【形】讨厌 구두…