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提供了三个主要方法:beforeBodyRead
、afterBodyRead
和handleEmptyBody
。
-
beforeBodyRead:这个方法在请求体被读取之前调用,主要用于预处理请求体。默认实现是直接返回传入的
HttpInputMessage
对象。 -
afterBodyRead:在请求体被读取并转换为对象之后调用,用于对读取到的对象进行进一步处理。默认实现是直接返回转换后的对象。
-
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接口加密呢? 1.防止爬虫 2.防止数据被串改 3.确保数据安全 2.如何实现接口加密呢? 3.我们可以使用哪些加密算法来加密呢? AES 密码学中的高级加密标准(Advanced Encryption Standard,…...

iOS在项目中设置 Dev、Staging 和 Prod 三个不同的环境
在 Objective-C 项目中设置 Dev、Staging 和 Prod 三个不同的环境,并为每个环境使用不同的 Bundle ID,可以通过以下步骤实现: 步骤 1: 创建不同的 Build Configuration 打开项目: 启动 Xcode 并打开你的项目。 选择项目文件&…...

openeuler24.09 系统无需配置 docker 源即可安装 docker 和 docker-composer
准备工作 1、准备一台刚刚创建的 openeuler24.09 lxc 虚拟机 2、使用 dnf 更新到最新,安装常用 工具 dnf update -y dnf install vim net-tools wget3、设置 ssh 由于ssh 与通常网上教程大同小异,在此我们就略过。 从下图我们可以看到 openeuler24.09 已经远程连接上。 …...

Flask入门:打造简易投票系统
目录 准备工作 创建项目结构 编写HTML模板 编写Flask应用 代码解读 进一步优化 结语 Flask,这个轻量级的Python Web框架,因其简洁和易用性,成为很多开发者入门Web开发的首选。今天,我们就用Flask来做一个简单的投票系统,让你快速上手Web开发,同时理解Flask的核心概…...

日常思考笔记
技术管理, 团队管理,人才培养,梯队建设 项目管理,项目全生命周期,项目进度 考核规范, AQS 是CountDownLatch,ReentrantLock,Semaphore,ReentrantReadWriteLock的基础 vo…...

【JAVA】后台管理系统密码复杂度和修改密码处理
一、后台管理系统密码要求 后台管理系统密码要求 口令有效期:90天 口令长度8位及8位以上 口令复杂度要求,至少包含以下四类字符中的三类字符: 英文大写字母(A 到 Z)、英文小写字母(a 到 z)、10个基本数字(0 到 9)、特殊字符(例如 !、$、#、%、、^、&a…...

微服务SpringCloud链路追踪之Micrometer+Zipkin
视频教程: https://www.bilibili.com/video/BV12LBFYjEvR 效果演示 当我们发送一个请求给 Gateway 的时候,由 Micrometer trace 进行链路追踪和数据收集,由 Zipkin 进行数据展示。可以清楚的看到微服务的调用过程,以及每个微服务…...

Quartz(2-Trigger)
相关文章链接 定时任务工具类(Cron Util)SpringBoot TaskQuartz(1-Job)Quartz(2-Trigger) Trigger 方法 优先级(priority) 如果你的 trigger 很多(或者 Quartz 线程…...

【微信小程序开发 - 3】:项目组成介绍
文章目录 项目组成介绍项目的基本组成结构小程序页面的组成部分JSON配置文件的作用app.json文件project.config.json文件sitemap.json文件页面的 .json 配置文件新建小程序页面修改项目首页 XWML模板XWML 和 HTML 的区别 WXSS样式WXSS 和 CSS 的区别 .js文件 项目组成介绍 项目…...

Leetcode 三角形最小路径和
算法思想与代码详解 这段代码采用的是**动态规划(Dynamic Programming)**的思想,用来解决“120. 三角形最小路径和”问题。动态规划通过将问题分解成更小的子问题,并通过保存子问题的解来避免重复计算,从而提高效率。…...

DataOps驱动数据集成创新:Apache DolphinScheduler SeaTunnel on Amazon Web Services
引言 在数字化转型的浪潮中,数据已成为企业最宝贵的资产之一。DataOps作为一种文化、流程和实践的集合,旨在提高数据管道的质量和效率,从而加速数据从源头到消费的过程。白鲸开源科技,作为DataOps领域的领先开源原生公司…...

Android Studio的笔记--BusyBox相关
BusyBox 相关 BusyBoxandroid上安装busybox和使用示例一、下载二、移动三、安装和设置环境变量四、使用 busybox源码下载和查看 BusyBox BUSYBOX BUSYBOX链接https://busybox.net/ 点击链接后如图 点击左边菜单栏的Get BusyBix中的Download Source 跳转到busybox 的下载源码…...

MySQL 存储过程与函数:增强数据库功能
一、MySQL 存储过程与函数概述 (一)存储过程的定义与特点 存储过程是一组预编译的 SQL 语句集合,它们被存储在数据库中,可根据需要被重复调用。例如,在一个电商系统中,经常需要查询某个时间段内的订单数据…...

网络安全(3)_安全套接字层SSL
4. 安全套接字层 4.1 安全套接字层(SSL)和传输层安全(TLS) (1)SSL/TLS提供的安全服务 ①SSL服务器鉴别,允许用户证实服务器的身份。支持SSL的客户端通过验证来自服务器的证书,来鉴别…...

Git 快速入门
Git 是什么? Git 是一个分布式版本控制系统四大区域: 工作区:项目文件的当前状态,即本地目录。暂存区:保存将要提交的文件快照,是一个中间层,使用git add将文件添加到暂存区。本地仓库…...

AI学习记录 - 依据 minimind 项目入门
想学习AI,还是需要从头到尾跑一边流程,最近看到这个项目 minimind, 我也记录下学习到的东西,需要结合项目的readme看。 1、github链接 https://github.com/jingyaogong/minimind?tabreadme-ov-file 2、硬件环境:英伟达4070ti …...

数据结构----链表头插中插尾插
一、链表的基本概念 链表是一种线性数据结构,它由一系列节点组成。每个节点包含两个主要部分: 数据域:用于存储数据元素,可以是任何类型的数据,如整数、字符、结构体等。指针域:用于存储下一个节点&#…...

设计模式-读书笔记
确认好: 模式名称 问题:在何时使用模式,包含设计中存在的问题以及问题存在的原因 解决方案:设计模式的组成部分,以及这些组成部分之间的相互关系,各自的职责和协作方式,用uml类图和核心代码描…...

c语言----选择结构
基本概念 选择结构是C语言中用于根据条件判断来执行不同代码块的结构。它允许程序在不同的条件下执行不同的操作,使程序具有决策能力。 if语句 单分支if语句 语法格式: if (条件表达式) { 执行语句块; } 功能: 当条件表达式的值为真&#…...

KS曲线python实现
目录 实战 实战 # 导入第三方模块 import pandas as pd import numpy as np import matplotlib.pyplot as plt# 自定义绘制ks曲线的函数 def plot_ks(y_test, y_score, positive_flag):# 对y_test重新设置索引y_test.index np.arange(len(y_test))# 构建目标数据集target_dat…...

解决matplotlib中文乱码问题
进入python,查看缓存 import matplotlib as mpl print(mpl.get_cachedir())如果结果为/Users/xxx/.matplotlib 那么就rm -rf /Users/xxx/.matplotlib 然后 mkdir ~/.fonts cd ~/.fonts wget http://129.204.205.246/downloads/SimHei.ttfsudo apt-get install fo…...

实操给桌面机器人加上超拟人音色
前面我们讲了怎么用CSK6大模型开发板做一个桌面机器人充当AI语音助理,近期上线超拟人方案,不仅大模型语音最快可以1秒内回复,还可以让我们的桌面机器人使用超拟人音色、具备声纹识别等能力,本文以csk6大模型开发板为例实操怎么把超…...

git stash 的文件如何找回
在Git中,如果你使用了git stash命令来保存你的工作进度,但之后想要找回这些被stash的文件,你可以按照以下步骤进行操作: 1. 查看stash列表 首先,使用git stash list命令来查看当前保存的所有stash记录。这个命令会列出…...

皮肤伤口分割数据集labelme格式248张5类别
数据集格式:labelme格式(不包含mask文件,仅仅包含jpg图片和对应的json文件) 图片数量(jpg文件个数):284 标注数量(json文件个数):284 标注类别数:5 标注类别名称:["bruises","burns","cu…...

uni-app开发AI康复锻炼小程序,帮助肢体受伤患者康复!
**提要:**近段时间我们收到多个康复机构用户,咨询AI运动识别插件是否可以应用于肢力运动受限患者的康复锻炼中来,插件是可以应用到AI康复锻炼中的,今天小编就为您介绍一下AI运动识别插件在康腹锻炼中的应用场景。 一、康复机构的应…...

双内核架构 Xenomai 4 安装教程
Xenomai 4是一种双内核架构, 继承了Xenomai系列的特点,通过在Linux内核中嵌入一个辅助核心(companion core),来提供实时能力。这个辅助核心专门处理那些需要极低且有界响应时间的任务。 本文将在官网教程(https://evlproject.org/…...

【redis的使用、账号流程、游戏服Handler的反射调用】1.自增id 2.全局用户名这样子名字唯一 3.
一、web服 1)账号注册 // 用于唯一命名服务 com.xinyue.game.center.business.account.logic.AccountRegisterService#accountRegister public void accountRegister(AccountEntity account) {accountManager.checkUsername(account.getUsername());accountManager.checkPass…...

neo4j 图表数据导入到 TuGraph
neo4j 图表数据导入到 TuGraph 代码文件说明后文 前言:近期在引入阿里的 TuGraph 图数据库,需要将 原 neo4j 数据导入到新的 tugraph 数据库中。预期走csv文件导入导出,但因为格式和数据库设计问题,操作起来比较麻烦(可能是个人没…...

启动报错java.lang.NoClassDefFoundError: ch/qos/logback/core/status/WarnStatus
报错信息图片 日志: Exception in thread "Quartz Scheduler [scheduler]" java.lang.NoClassDefFoundError: ch/qos/logback/core/status/WarnStatus先说我自己遇到的问题,我们项目在web设置了自定义的log输出路径,多了一个 / 去…...

【ubuntu18.04】ubuntu18.04挂在硬盘出现 Wrong diagnostic page; asked for 1 got 8解决方案
错误日志 [ 8754.700227] usb 2-3: new full-speed USB device number 3 using xhci_hcd [ 8754.867389] usb 2-3: New USB device found, idVendor0e0f, idProduct0002, bcdDevice 1.00 [ 8754.867421] usb 2-3: New USB device strings: Mfr1, Product2, SerialNumber0 [ 87…...