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

springboot结合AES和国密SM4进行接口加密

api接口加密

1.为什么需要api接口加密呢?

1.防止爬虫

2.防止数据被串改

3.确保数据安全

2.如何实现接口加密呢?

3.我们可以使用哪些加密算法来加密呢?

AES

密码学中的高级加密标准(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采用的一种区块加密标准。

SM4

SM4.0(原名SMS4.0)是中华人民共和国政府采用的一种分组密码标准,由国家密码管理局于2012年3月21日发布。相关标准为“GM/T 0002-2012《SM4分组密码算法》(原SMS4分组密码算法)”。在商用密码体系中,SM4主要用于数据加密,其算法公开,分组长度与密钥长度均为128bit,加密算法与密钥扩展算法都采用32轮非线性迭代结构,S盒为固定的8比特输入8比特输出。SM4.0中的指令长度被提升到大于64K(即64×1024)的水平,这是SM 3.0规格(渲染指令长度允许大于512)的128倍。

4.具体实现
4.1后端

在这之前我们先了解一个类RequestBodyAdviceAdapter,主要通过该类实现~

RequestBodyAdviceAdapter

官网解释:RequestBodyAdviceAdapter (Spring Framework 6.2.1 API)

RequestBodyAdviceAdapter‌是Spring MVC中用于增强请求体处理的一个工具类,它实现了RequestBodyAdvice接口。RequestBodyAdviceAdapter提供了三个主要方法:beforeBodyReadafterBodyReadhandleEmptyBody

  1. beforeBodyRead‌:这个方法在请求体被读取之前调用,主要用于预处理请求体。默认实现是直接返回传入的HttpInputMessage对象。

  2. afterBodyRead‌:在请求体被读取并转换为对象之后调用,用于对读取到的对象进行进一步处理。默认实现是直接返回转换后的对象。

  3. handleEmptyBody‌:当请求体为空时调用,用于处理这种情况。默认实现是直接返回null。

代码实现

这里我们使用类似Aop的思想实现,定义对应的注解!

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ApiDecrypt { //解密
}
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface ApiEncrypt {//加密
}
​
@Slf4j
@ControllerAdvice
public class DecryptRequestAdvice extends RequestBodyAdviceAdapter {
​private static final String ENCODING = "UTF-8";
​@Resourceprivate ApiEncryptService apiEncryptService;
​@Overridepublic boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return methodParameter.hasMethodAnnotation(ApiDecrypt.class) || methodParameter.hasParameterAnnotation(ApiDecrypt.class)|| methodParameter.getContainingClass().isAnnotationPresent(ApiDecrypt.class);}
​@Overridepublic HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,Type targetType, Class<? extends HttpMessageConverter<?>> converterType)  {try {String bodyStr = IOUtils.toString(inputMessage.getBody(), ENCODING);ApiEncryptForm apiEncryptForm = JSONObject.parseObject(bodyStr, ApiEncryptForm.class);if (StrUtil.isEmpty(apiEncryptForm.getEncryptData())) {return inputMessage;}String decrypt = apiEncryptService.decrypt(apiEncryptForm.getEncryptData());return new DecryptHttpInputMessage(inputMessage.getHeaders(), IOUtils.toInputStream(decrypt, ENCODING));} catch (Exception e) {return inputMessage;}
​}
​@Overridepublic Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return body;}
​@Overridepublic Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {return body;}
​static class DecryptHttpInputMessage implements HttpInputMessage {private final HttpHeaders headers;
​private final InputStream body;
​public DecryptHttpInputMessage(HttpHeaders headers, InputStream body) {this.headers = headers;this.body = body;}
​@Overridepublic InputStream getBody() {return body;}
​@Overridepublic HttpHeaders getHeaders() {return headers;}}
​
}
@Slf4j
@ControllerAdvice
public class EncryptResponseAdvice implements ResponseBodyAdvice<ResponseDTO> {
​@Resourceprivate ApiEncryptService apiEncryptService;
​@Resourceprivate ObjectMapper objectMapper;
​@Overridepublic boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {return returnType.hasMethodAnnotation(ApiEncrypt.class) || returnType.getContainingClass().isAnnotationPresent(ApiEncrypt.class);}
​@Overridepublic ResponseDTO beforeBodyWrite(ResponseDTO body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {if (body.getData() == null) {return body;}
​String encrypt = null;try {encrypt = apiEncryptService.encrypt(objectMapper.writeValueAsString(body.getData()));} catch (JsonProcessingException e) {throw new RuntimeException(e);}body.setData(encrypt);body.setDataType(DataTypeEnum.ENCRYPT.getValue());return body;}
}

实现类

/*** AES 加密和解密* 1、AES加密算法支持三种密钥长度:128位、192位和256位,这里选择128位* 2、AES 要求秘钥为 128bit,转化字节为 16个字节;* 3、js前端使用 UCS-2 或者 UTF-16 编码,字母、数字、特殊符号等 占用1个字节;* 4、所以:秘钥Key 组成为:字母、数字、特殊符号 一共16个即可*/
@Slf4j
public class ApiEncryptServiceAesImpl implements ApiEncryptService {
​private static final String CHARSET = "UTF-8";
​private static final String AES_KEY = "1024lab__1024lab";
​static {Security.addProvider(new BouncyCastleProvider());}
​@Overridepublic String encrypt(String data) {try {//  AES 加密 并转为 base64AES aes = new AES(hexToBytes(stringToHex(AES_KEY)));return aes.encryptBase64(data);} catch (Exception e) {log.error(e.getMessage(), e);return StringConst.EMPTY;}}
​@Overridepublic String decrypt(String data) {try {// 第一步: Base64 解码byte[] base64Decode = Base64.getDecoder().decode(data);// 第二步: AES 解密AES aes = new AES(hexToBytes(stringToHex(AES_KEY)));byte[] decryptedBytes = aes.decrypt(base64Decode);return new String(decryptedBytes, CHARSET);} catch (Exception e) {log.error(e.getMessage(), e);return StringConst.EMPTY;}}
​/*** 16 进制串转字节数组** @param hex 16进制字符串* @return byte数组*/public static byte[] hexToBytes(String hex) {int length = hex.length();byte[] result;if (length % 2 == 1) {length++;result = new byte[(length / 2)];hex = "0" + hex;} else {result = new byte[(length / 2)];}int j = 0;for (int i = 0; i < length; i += 2) {result[j] = hexToByte(hex.substring(i, i + 2));j++;}return result;}
​public static String stringToHex(String input) {char[] chars = input.toCharArray();StringBuilder hex = new StringBuilder();for (char c : chars) {hex.append(Integer.toHexString((int) c));}return hex.toString();}
​/*** 16 进制字符转字节** @param hex 16进制字符 0x00到0xFF* @return byte*/private static byte hexToByte(String hex) {return (byte) Integer.parseInt(hex, 16);}
​public static void main(String[] args) {
//        System.out.println(new ApiEncryptServiceAesImpl().encrypt("{\"name\":\"chen\",\"age\":18}"));System.out.println(new ApiEncryptServiceAesImpl().decrypt("MWFjZTI3MDA0ZDcxY2RlM2U4YTY5NDU3MjE3MmE0YzlhOTI0OTVhOTBiM2VmYzYwZjgxZTlmMjRmNDVkMWNlMg=="));}
}
​
​
@Slf4j
@Service
public class ApiEncryptServiceSmImpl implements ApiEncryptService {
​private static final String CHARSET = "UTF-8";private static final String SM4_KEY = "1024lab__1024lab";
​static {Security.addProvider(new BouncyCastleProvider());}
​
​@Overridepublic String encrypt(String data) {try {
​// 第一步: SM4 加密SM4 sm4 = new SM4(hexToBytes(stringToHex(SM4_KEY)));String encryptHex = sm4.encryptHex(data);
​// 第二步: Base64 编码return new String(Base64.getEncoder().encode(encryptHex.getBytes(CHARSET)), CHARSET);
​} catch (Exception e) {log.error(e.getMessage(), e);return StringConst.EMPTY;}}
​
​@Overridepublic String decrypt(String data) {try {
​// 第一步: Base64 解码byte[] base64Decode = Base64.getDecoder().decode(data);
​// 第二步: SM4 解密SM4 sm4 = new SM4(hexToBytes(stringToHex(SM4_KEY)));return sm4.decryptStr(new String(base64Decode));
​} catch (Exception e) {log.error(e.getMessage(), e);return StringConst.EMPTY;}}
​
​public static String stringToHex(String input) {char[] chars = input.toCharArray();StringBuilder hex = new StringBuilder();for (char c : chars) {hex.append(Integer.toHexString((int) c));}return hex.toString();}
​
​/*** 16 进制串转字节数组** @param hex 16进制字符串* @return byte数组*/public static byte[] hexToBytes(String hex) {int length = hex.length();byte[] result;if (length % 2 == 1) {length++;result = new byte[(length / 2)];hex = "0" + hex;} else {result = new byte[(length / 2)];}int j = 0;for (int i = 0; i < length; i += 2) {result[j] = hexToByte(hex.substring(i, i + 2));j++;}return result;}
​/*** 16 进制字符转字节** @param hex 16进制字符 0x00到0xFF* @return byte*/private static byte hexToByte(String hex) {return (byte) Integer.parseInt(hex, 16);}
​
}
4.2前端

axios.js

import axios from 'axios'
import { decryptData, encryptData } from './encrypt.js';
​
const requests = axios.create({//共同的接口前缀baseURL: 'http://localhost:8881',//超时时间timeout: 2000
});
​
//请求拦截器:再发请求之前,请求拦截器可以检查到,可以在请求发出去之前做一些事情
requests.interceptors.request.use(config => {return config
}), err => {return Promise.reject(err)
}
​
//响应拦截器
requests.interceptors.response.use(response => {// 如果是加密数据if (response.data.dataType === 10) {response.data.encryptData = response.data.data;let decryptStr = decryptData(response.data.data);if (decryptStr) {response.data.data = JSON.parse(decryptStr);}}return response.data
}, err => {return Promise.reject(err)
})
​
// ================================= 加密 =================================
/*** 加密请求参数的post请求*/
export const postEncryptRequest = (url, data) => {return requests({data: { encryptData: encryptData(data) },url,method: 'post',});
};
​
​
/*** post请求*/
export const postRequest = (url, data) => {return requests({data,url,method: 'post',});
};
​
/*** get请求  /api?encryptData=xxx*/
export const getEncryptRequest = (url, data) => {return requests({params: { encryptData: encryptData(data) },url,method: 'get',});
};

前端加密

import CryptoJS from 'crypto-js';
import CryptoSM from 'sm-crypto';
​
function object2string(data) {if (typeof data === 'object') {return JSON.stringify(data);}
​let str = JSON.stringify(data);if (str.startsWith("'") || str.startsWith('"')) {str = str.substring(1);}if (str.endsWith("'") || str.endsWith('"')) {str = str.substring(0, str.length - 1);}return str;
}
/*** 字符串转为数字*/
function stringToHex(str) {let hex = '';for (let i = 0; i < str.length; i++) {hex += str.charCodeAt(i).toString(16).padStart(2, '0');}return hex;
}
​
/** -------------------- ※ AES 加密、解密 begin ※ --------------------** 1、AES加密算法支持三种密钥长度:128位、192位和256位,这里选择128位* 2、AES 要求秘钥为 128bit,转化字节为 16个字节;* 3、js前端使用 UCS-2 或者 UTF-16 编码,字母、数字、特殊符号等 占用1个字节;* 4、所以:秘钥Key 组成为:字母、数字、特殊符号 一共16个即可** -------------------- ※ AES 加密、解密 end ※ --------------------*/
const AES_KEY = '1024lab__1024lab';
const AES = {encryptData: function (data) {// AES 加密 并转为 base64let utf8Data = CryptoJS.enc.Utf8.parse(object2string(data));const key = CryptoJS.enc.Utf8.parse(AES_KEY);const encrypted = CryptoJS.AES.encrypt(utf8Data, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7,});return CryptoJS.enc.Base64.stringify(encrypted.ciphertext);},
​decryptData: function (data) {//  第一步:Base64 解码let words = CryptoJS.enc.Base64.parse(data);
​// 第二步:AES 解密const key = CryptoJS.enc.Utf8.parse(AES_KEY);return CryptoJS.AES.decrypt({ ciphertext: words }, key, {mode: CryptoJS.mode.ECB,padding: CryptoJS.pad.Pkcs7,}).toString(CryptoJS.enc.Utf8);},
};
​
/** -------------------- ※ 国密SM4算法 加密、解密 begin ※ --------------------** 1、国密SM4 要求秘钥为 128bit,转化字节为 16个字节;* 2、js前端使用 UCS-2 或者 UTF-16 编码,字母、数字、特殊符号等 占用1个字节;* 3、java中 每个 字母数字 也是占用1个字节;* 4、所以:前端和后端的 秘钥Key 组成为:字母、数字、特殊符号 一共16个即可** -------------------- ※ 国密SM4算法 加密、解密 end ※ --------------------*/
​
// 秘钥Key 组成为:字母、数字、特殊符号 一共16个即可
const SM4_KEY = '1024lab__1024lab';
​
const SM4 = {encryptData: function (data) {// 第一步:SM4 加密let encryptData = CryptoSM.sm4.encrypt(object2string(data), stringToHex(SM4_KEY));// 第二步: Base64 编码return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encryptData));},
​decryptData: function (data) {// 第一步:Base64 解码let words = CryptoJS.enc.Base64.parse(data);let decode64Str = CryptoJS.enc.Utf8.stringify(words);
​// 第二步:SM4 解密return CryptoSM.sm4.decrypt(decode64Str, stringToHex(SM4_KEY));},
};
​
​
​
// -----------------------  对外暴露: 加密、解密 -----------------------
​
// 默认使用SM4算法
const EncryptObject = SM4;
// const EncryptObject = AES;
​
/*** 加密*/
export const encryptData = function (data) {return !data ? null : EncryptObject.encryptData(data);
};
/*** 解密*/
export const decryptData = function (data) {return !data ? null : EncryptObject.decryptData(data);
};

api.js

import { postRequest, postEncryptRequest, getEncryptRequest } from '@/utils/axios.js';
// 测试获取列表 双向加密
export const getList = (data) => {return postEncryptRequest('/api/test/getList',data)
}
​
export const getListOfTetsGet = (data) => {return getEncryptRequest('/api/testget/getList',data)
}
​
// 请求加密paramsAes
export const getListParamsAes = (data) => {return postEncryptRequest('/api/paramsAes/getList',data)
}
​
// 响应加密  /resAes/getList
export const getListResAes = (data) => {return postRequest('/api/resAes/getList',data)
}

vue界面

<template><div><el-button type="primary" @click="aes">发送加密请求</el-button><el-button type="primary" @click="paramsAsc">请求体加密</el-button><el-button type="primary" @click="reqAsc">返回加密</el-button>
​<div><el-table :data="tableData" border style="width: 100%"><el-table-column prop="title" label="书标题"></el-table-column><el-table-column prop="author" label="作者"></el-table-column><el-table-column prop="price" label="价格"></el-table-column><el-table-column fixed="right" label="操作" width="100"><template slot-scope="scope"><el-button @click="handleClick(scope.row)" type="text" size="small">查看</el-button><el-button type="text" size="small">编辑</el-button></template></el-table-column></el-table></div></div>
</template>
​
<script>import { getList, getListParamsAes, getListResAes, getListOfTetsGet } from "@/api/test.js"export default {name: 'HelloWorld',props: {msg: String},data() {return {tableData: []}},mounted() {this.aes()},methods: {aes() {getList({ pageNum: 1, pageSize: 3 }).then(res => {if (res.code == 0) {this.tableData = res.data}})},paramsAsc() {getListParamsAes({ pageNum: 1, pageSize: 3 }).then(res => {console.log(res);})},reqAsc() {getListResAes({ pageNum: 1, pageSize: 3 }).then(res => {console.log(res);})}},}
</script>
5.测试结果

双向加密

 

请求体加密

 

响应体加密

 

6.声明

该案例参考于开源的smartadmin项目,该项目类似于若依,整体代码比较规范,推荐使用~

官网地址:SmartAdmin | 1024创新实验室

gitee地址:smart-admin(MIT协议-免费任意商用): 🔥SmartAdmin以「高质量代码」为核心,「简洁、高效、安全」的快速开发平台;基于SpringBoot2/3+Sa-Token+Mybatis-Plus和Vue3 +Ant Design Vue+UniApp (提供JavaScript和TypeScript双版本、Java8和java17双版本);满足三级等保、网络安全、数据安全等功能要求。并重磅开源千余家企业在使用的《高质量代码规范》等

相关文章:

springboot结合AES和国密SM4进行接口加密

api接口加密 1.为什么需要api接口加密呢&#xff1f; 1.防止爬虫 2.防止数据被串改 3.确保数据安全 2.如何实现接口加密呢&#xff1f; 3.我们可以使用哪些加密算法来加密呢&#xff1f; AES 密码学中的高级加密标准&#xff08;Advanced Encryption Standard&#xff0c;…...

后端项目java中字符串、集合、日期时间常用方法

我这里只介绍了项目中最常用的哈,比如像集合有很多,但我们最常用的就是ArrayList。 然后我这里会以javascript中的字符串、数组的方法为基准来实现,有些方法js和java会有些区别也会介绍 字符串 每次修改 String 对象都会创建一个新的对象,而 StringBuffer 可以在同一个对象…...

前端框架Vue的路由机制

大家好&#xff0c;我是G探险者。 最近在调试前端代码的时候&#xff0c;遇到一个问题。首先我们有一个门户页面&#xff0c;该页面里面有很多的豆腐块&#xff0c;每个豆腐块会配置一个系统的跳转连接。 我的系统就是其中一个豆腐块&#xff0c;我第一次登录进来之后&#xf…...

flutter 快速实现侧边栏

首先我们写一个侧边栏工具类&#xff0c;示例如下&#xff1a; import package:flutter/material.dart;class Sidebar extends StatelessWidget {overrideWidget build(BuildContext context) {return Drawer(child: ListView(padding: EdgeInsets.zero,children: <Widget&…...

华为数通最新题库 H12-821 HCIP稳定过人中

以下是成绩单和考试人员 HCIP H12-831 HCIP H12-725 安全中级...

算法训练第二十三天|93. 复原 IP 地址 78. 子集 90. 子集 II

93. 复原 IP 地址--分割 题目 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 . 分隔。 例如&#xff1a;"0.1.2.201" 和 "192.168.1.1" 是 有效 IP 地址&…...

JS,递归,处理树形数据组件,模糊查询树形结构数据字段

JS递归如何模糊查询树形结构数据,根据数据中的某一个字段值&#xff0c;模糊匹配 直接拿去使用就行 function filterTreeLabel(arr, label) {let result []arr.forEach((item) > {// if (String(item.POBJECT_NAME).toLowerCase().indexOf(label)!-1) {if (String(item.P…...

前端大数字精度丢失?Choerodon UI 大数字解决方案:精确性与灵活性的结合!

01 引言 在企业项目开发中&#xff0c;数据的精确性是关键。Choerodon UI 的大数字解决方案&#xff0c;通过其高精度计算、数据一致性维护、灵活的数据交互、国际化支持、兼容性保障、定制化格式化等优势&#xff0c;为开发人员提供了一个强大的武器库&#xff0c;以确保在处…...

matlab凸包检测

% 创建一个3D点集 points [1 2 3; 4 5 6; 7 8 9; 10 11 12; 13 14 15]; % 使用convhull函数计算凸包 hull convhull(points); % 输出凸包点的索引 disp(Convex Hull Indices:); disp(hull); % 绘制点集和凸包 figure; scatter3(points(:,1), points(:,2), points(:,3),…...

单节点calico性能优化

在单节点上部署calicov3273后&#xff0c;发现资源占用 修改calico以下配置是资源消耗降低 1、因为是单节点&#xff0c;没有跨节点pod网段组网需要&#xff0c;禁用overlay方式网络(ipip&#xff0c;vxlan),使用route方式网络 配置calico-node的环境变量 CALICO_IPV4POOL_I…...

【芯片设计- RTL 数字逻辑设计入门 番外篇 7.1 -- 基于ATE的IC测试原理】

文章目录 ATE 测试概述Opens/Shorts测试Leakage测试AC测试转自:漫谈大千世界 漫谈大千世界 2024年10月23日 23:17 湖北 ATE 测试概述 ATE(Automatic Test Equipment)是用于检测集成电路(IC)功能完整性的自动测试设备。它在半导体产业中扮演着至关重要的角色,主要用于检…...

oracle 导入数据提示跳过表

imp system/orclorcl fileD:\oracle_back.dmp fully showy logD:\oracle_log.log 今天用上面的命令往 oracle 中导入数据出现一个奇怪的问题 就是所有导入的表都提示 正在跳过表XXX 最后提示成功终止导入, 没有出现警告。 最后select一个表也没导入进来 怪哉怪哉&#xff01;…...

鸿蒙开发(15)案例 排行榜

排行榜 准备图片 定义案例需要的数据模型 创建Models文件&#xff0c; //定义app需要的数据模型export class FruitData{name:string;vote:string;id:string;constructor(id:string,name:string,vote:string,) {this.id idthis.name namethis.vote vote}}排行榜头部 创…...

【Java Web】Axios实现前后端数据异步交互

目录 一、Promise概述 二、Promise基本用法 三、async和await关键字 四、Axios介绍 4.1 Axios基本用法 4.2 Axios简化用法之get和post方法 五、Axios拦截器 六、跨域问题处理 一、Promise概述 axios是代替原生的ajax实现前后端数据交互的一套新解决方案&#xff0c;而…...

SLAAC如何工作?

SLAAC如何工作&#xff1f; IPv6无状态地址自动配置(SLAAC)-常见问题 - 苍然满关中 - 博客园 https://support.huawei.com/enterprise/zh/doc/EDOC1100323788?sectionj00shttps://www.zhihu.com/question/6691553243/answer/57023796400 主机在启动或接口UP后&#xff0c;发…...

微信小程序UI自动化测试实践 !

微信小程序UI自动化测试实践 引言&#xff1a; 随着微信小程序的快速发展&#xff0c;越来越多的企业和开发者开始开发小程序来满足用户的需求。而在开发小程序的过程中&#xff0c;UI自动化测试是一个必不可少的环节&#xff0c;可以帮助开发者减少人工测试的工作量&#xff…...

代码随想录-笔记-其七

我们来到了贪心算法的章节。 贪心算法和其他部分不太一样的是&#xff0c;他更多的是突出一种思路&#xff1a;通过求局部最优解来求全局最优解。因为只是一个大的思想逻辑&#xff0c;针对不同题型总是有不同的解决方案&#xff0c;所以贪心算法也不想其他算法那样有一个很经…...

react身份证回显

1. 处理身份证号的函数 function getAgeSexAndBirthdate(idCard: string): { sex: 男 | 女 | null; birthdate: Date | null } {if (idCard.length ! 18) {console.error(身份证号码必须是18位。);return { sex: null, birthdate: null };}// 提取出生年月日const year parse…...

Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构

大家好&#xff0c;今天&#xff0c;我们将讨论 Hibernate 和 JPA 架构。 在开始我们的文章之前&#xff0c;我想回答一个重要的问题&#xff1a;为什么我们需要使用 Hibernate、Eclipse Link、EF core 等 ORM 工具&#xff1f; 事实上&#xff0c;这是一个非常好的问题。我们…...

leetcode----mysql

1179. 重新格式化部门表 - 力扣&#xff08;LeetCode&#xff09; 表 Department&#xff1a; ------------------------ | Column Name | Type | ------------------------ | id | int | | revenue | int | | month | varchar | ----…...

盛元广通畜牧与水产品检验技术研究所LIMS系统

一、系统概述 盛元广通畜牧与水产品检验技术研究所LIMS系统集成了检测流程管理、样品管理、仪器设备管理、质量控制、数据记录与分析、合规性管理等功能于一体&#xff0c;能够帮助实验室实现全流程的数字化管理。在水产、畜牧产品的质检实验室中&#xff0c;LIMS系统通过引入…...

EXCEL文件解析

[Excel文件名].xlsx (解压后) │ ├── _rels │ └── .rels (定义关系文件) ├── docProps │ ├── app.xml (应用程序属性) │ └── core.xml (核心文档属性) ├── xl │ ├── _rels │ │ └── workbook.xml.rels (工作簿关系文件) │ ├── …...

【C++】- 掌握STL List类:带你探索双向链表的魅力

文章目录 前言&#xff1a;一.list的介绍及使用1. list的介绍2. list的使用2.1 list的构造2.2 list iterator的使用2.3 list capacity2.4 list element access2.5 list modifiers2.6 list的迭代器失效 二.list的模拟实现1. list的节点2. list的成员变量3.list迭代器相关问题3.1…...

开源 AI 智能名片 S2B2C 商城小程序中运营与产品的关系剖析

摘要&#xff1a;本文聚焦于开源 AI 智能名片 S2B2C 商城小程序&#xff0c;深入探讨其中运营与产品之间的关系。通过分析运营与产品的多种关系认知&#xff0c;阐述在该特定小程序情境下运营与产品相互依存、相互作用的机制&#xff0c;包括运营对产品的需求以及产品对运营的依…...

flask_socketio 以继承 Namespace方式实现一个网页聊天应用

点击进入上一篇&#xff0c;可作为参考 实验环境 python 用的是3.11.11 其他环境可以通过这种方式一键安装&#xff1a; pip install flask3.1.0 Flask-SocketIO5.4.1 gevent-websocket0.10.1 -i https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple pip list 详情如下&am…...

DePIN潜力项目Spheron解读:激活闲置硬件,赋能Web3与AI

DePIN赛道作为今年加密资本关注的热点之一&#xff0c;不仅吸引了大量资金涌入&#xff0c;还凭借其灵活的资源调配、高效的运作方式和可靠的安全性能&#xff0c;逐渐渗透到多个领域和项目中。例如&#xff0c;Helium的无线网络协议、IoTeX的去中心化物联网、IO NET的去中心化…...

《Vue进阶教程》第十六课:深入完善响应式系统之单例模式

往期内容&#xff1a; 《Vue进阶教程》第五课&#xff1a;ref()函数详解(重点) 《Vue进阶教程》第六课&#xff1a;computed()函数详解(上) 《Vue进阶教程》第七课&#xff1a;computed()函数详解(下) 《Vue进阶教程》第八课&#xff1a;watch()函数的基本使用 《Vue进阶教…...

C++ —— const修饰指针

C —— const修饰指针 常量指针&#xff08;实际开发中用的很多&#xff09;指针常量&#xff08;了解即可&#xff09;常指针常量&#xff08;了解即可&#xff09; 常量指针&#xff08;实际开发中用的很多&#xff09; 语法&#xff1a;const 数据类型 *变量名; 不能通过解…...

【学习笔记】数据结构(八)

动态存储管理 文章目录 动态存储管理8.1 概述8.2 可利用空间表及分配方法8.3 边界标识法8.3.1 可利用空间表的结构8.3.2 分配算法8.3.3 回收算法 8.4 伙伴系统8.4.1 可利用空间表的结构8.4.2 分配算法8.4.3 回收算法 8.5 无用单元收集 - 垃圾回收机制8.6 存储紧缩 - 内存碎片化…...

maven-resources-production:ratel-fast: java.lang.IndexOutOfBoundsException

Maven生产环境中遇到java.lang.IndexOutOfBoundsException的问题&#xff0c;尝试了重启电脑、重启IDEA等常规方法无效&#xff0c;最终通过直接重建工程解决了问题。 Rebuild Project 再启动OK...

建投数据与腾讯云数据库TDSQL完成产品兼容性互认证

近日&#xff0c;经与腾讯云联合测试&#xff0c;建投数据自主研发的人力资源信息管理系统V3.0、招聘管理系统V3.0、绩效管理系统V2.0、培训管理系统V3.0通过腾讯云数据库TDSQL的技术认证&#xff0c;符合腾讯企业标准的要求&#xff0c;产品兼容性良好&#xff0c;性能卓越。 …...

后端-添加购物车和查看购物车

...

【HarmonyOS NEXT】Web 组件的基础用法以及 H5 侧与原生侧的双向数据通讯

关键词&#xff1a;鸿蒙、ArkTs、Web组件、通讯、数据 官方文档Web组件用法介绍&#xff1a;文档中心 Web 组件加载沙箱中页面可参考我的另一篇文章&#xff1a;【HarmonyOS NEXT】 如何将rawfile中文件复制到沙箱中_鸿蒙rawfile 复制到沙箱-CSDN博客 目录 如何在鸿蒙应用中加…...

7-2 排序

输入一批未排序的数据&#xff0c;数量不超过30个&#xff0c;请使用选择法或者冒泡法对其排序&#xff0c;并按照规定的要求输出。 输入格式: 先输入待排序的整形数的个数&#xff1b;然后输入所有的待排序的数据。 输出格式: 在一行中按照由大到小的顺序输出排序好的数据…...

Java通过反射破坏单例模式

有个第三方工具类&#xff0c;不支持多例模式。但是又不能直接改第三方工具类的代码&#xff0c;因此可以通过反射破坏第三方工具类的单例。 第三方工具类反编译如下 可以看到构造函数进行了私有化&#xff0c;不允许外部new&#xff0c;只能通过newInstance进行实例化。并且…...

FFmpeg第一话:FFmpeg 简介与环境搭建

FFmpeg 探索之旅 一、FFmpeg 简介与环境搭建 二、FFmpeg 解码详解 第一话&#xff1a;FFmpeg 简介与环境搭建 FFmpeg 探索之旅一、前言二、FFmpeg 是什么&#xff1f;三、简单介绍其历史背景四、为什么用 C学习 FFmpeg&#xff1f;&#xff08;一&#xff09;高性能优势&#…...

C++并发编程: std::atomic对指针进行操作

std::atomic 对指针进行运算的用途 std::atomic 提供了一种在多线程环境中安全地操作指针的方法。这对于实现线程安全的指针管理、动态内存分配、链表操作等场景非常有用。通过使用std::atomic对指针进行运算&#xff0c;可以确保指针操作的原子性和多线程安全性。 常见用途 …...

工业大数据分析算法实战-day08

文章目录 day08模型评价聚类算法基于距离的聚类基于层次的聚类基于密度的聚类基于分布的聚类聚类结果的评价 day08 今天是第8天&#xff0c;昨日阐述了概率图模型和集成学习的分类&#xff0c;主要讲解了有向图和无向图&#xff0c;生成式模型和判断式模型&#xff0c;以及集成…...

在 C# 中实现的目录基础操作

前言 在开发应用程序过程中&#xff0c;对操作系统上的文件夹存储文件和子文件夹操作是常见的需求。.NET中的Directory类提供了处理文件目录的功能。本文介绍如何读取文件夹的属性、获取文件夹的大小及文件个数、创建文件夹、遍历文件夹中的所有文件、移动文件夹和删除文件夹等…...

​Python 标识符是啥?​

Python 的标识符就是我们写代码时用来给变量、函数、类等取名字的东西。 你写的 my_variable 是个标识符&#xff0c; 定义的 add_numbers 函数名也是个标识符&#xff0c; 甚至你写的 Cat 类名&#xff0c;也是标识符。 一句话总结&#xff1a;标识符就是代码里给“东西”起…...

WEB开发: 全栈工程师起步 - Python Flask +SQLite的管理系统实现

一、前言 罗马不是一天建成的。 每个全栈工程师都是从HELLO WORLD 起步的。 之前我们分别用NODE.JS 、ASP.NET Core 这两个框架实现过基于WebServer的全栈工程师入门教程。 今天我们用更简单的来实现&#xff1a; Python。 我们将用Python来实现一个学生管理应用&#xff0…...

将 Matplotlib 图形转换为 PIL 图像并返回

将 Matplotlib 图形转换为 PIL 图像并返回 前言完整代码Matplotlib 中 fig 和 ax 的关系示例&#xff1a; 问题分析常见错误及解决方案总结 前言 Matplotlib 是 Python 里最流行的图表展示库&#xff0c;PIL (Python Imaging Library)则是一个强大的图像处理库。在开发过程中&…...

F5中获取客户端ip地址(client ip)

当F5设备对其原始设置上的所有IP地址使用NAT时&#xff0c;连接到poo成员&#xff08;nodes、backend servers&#xff09;的出站连接将是NAT IP地址。 pool 成员&#xff08;nodes、backend servers&#xff09;将无法看到真实的客户端 ip地址&#xff0c;因为看到的是F5上的…...

点焊机器人维修-ABB-KUKA-FANUC-YASKAWA

在正式启用点焊机器人之前&#xff0c;一项至关重要的预备步骤便是进行焊枪的全面设置操作。以FANUC机器人为例&#xff0c;其焊枪的设置流程涵盖了多个关键环节&#xff0c;如焊枪运动方向的精确规划、焊枪规格的选择以及零点标定的细致执行等。这些设置均须严格依据实际所采用…...

springboot448教学辅助系统(论文+源码)_kaic

摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。它让信息都可以通过网络传播&#xff0c;搭配信息管理工具可以很好地为人们提供服务。针对信息管理混乱&#xff0c;出错率高&#xff0c;信息安全性差&#x…...

网络地址转换(NAT)和端口映射

1. 网络地址转换(NAT) 1.1 NAT的应用场景 &#xff08;1&#xff09;应用场景&#xff1a;允许将私有IP地址映射到公网地址&#xff0c;以减缓IP地址空间的消耗 ①需要连接Internet&#xff0c;但主机没有公网IP地址 ②更换了一个新的ISP&#xff0c;需要重新组织网络时&…...

iClent3D for Cesium 实现无人机巡检飞行效果

作者&#xff1a;gaogy 1、背景 随着地理信息技术的发展&#xff0c;三维地球技术逐渐成为了许多领域中的核心工具&#xff0c;尤其是在城市规划、环境监测、航空航天以及军事领域。三维地图和场景的应用正在帮助人们更加直观地理解空间数据&#xff0c;提供更高效的决策支持。…...

后端- spring cache框架操作缓存数据(底层可以是redis,也可以是caffeine和ehcache)

...

低比特语言模型 是一种利用较少比特数进行语言建模的技术

Vanilla LLM: 基础的全精度语言模型&#xff0c;通常在较高比特数下运作 Vanilla LLM&#xff0c;或称为“基础的全精度语言模型”&#xff0c;是指使用标准的浮点数&#xff08;通常是16位或32位&#xff09;进行训练和推理的语言模型。这些模型依赖于经典的神经网络结构&…...

ES6中的map和set

Set ES6 提供了新的数据结构 Set。它类似于数组&#xff0c;但是成员的值都是唯一的&#xff0c;没有重复的值。 Set本身是一个构造函数&#xff0c;用来生成 Set 数据结构。 以下代码 const s new Set();[2, 3, 5, 4, 5, 2, 2].forEach(x > s.add(x));for (let i of s…...