【SpringBootStarter】自定义全局加解密组件
【SpringBootStarter】
目的
- 了解
SpringBoot Starter相关概念以及开发流程 - 实现自定义
SpringBoot Starter(全局加解密) - 了解测试流程
- 优化
最终引用的效果:
<dependency><groupId>com.xbhog</groupId><artifactId>globalValidation-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>
【开源地址】https://gitee.com/xbhog/encry-adecry-spring-boot-starter;欢迎star
了解SpringBoot Starter相关概念以及开发流程
SpringBoot Starter
SpringBoot Starter作用将一组相关的依赖打包,简化项目的配置和初始化过程,通过特定的Starter开发者可以快速的实现特定功能模块的开发和扩展。
自定义Starter能够促进团队内部资源的复用,保持项目间的一致性,提升协作效率并且有助于构建稳定、高效的大型系统。
开发流程
注入SpringBoot的方式
在刚开始开发Starter的时候,首先考虑的是怎么能注入到SpringBoot中?
这部分涉及到部分SpringBoot的自动装配原理,不太清楚的朋友可以补习下;
注入SpringBoot需要配置文件,在项目中的resources资源目录中创建该目录和文件。
demo-spring-boot-starter
└── src└── main└── java└── com.xbhog├── DemoBean.java└── DemoBeanConfig.java└── resources└── META-INF└── spring.factories
在spring.factories中我们指定一下自动装配的配置类,格式如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xbhog.DemoBeanConfig
/*** @author xbhog* @describe:*/
@Slf4j
@Configuration
public class DemoBeanConfig {@Beanpublic DemoBean getDemo() {log.info("已经触发了配置类,正在初始化DemoBean...");return new DemoBean();}
}
@Slf4j
public class DemoBean {public void getDemo(){log.info("方法调用成功");}
}
这样就可以将设置的包扫描路径下的相关操作打包到SpringBoot 中。
SpringBoot主类启动器:初始化的操作,感兴趣的朋友可以研究下


完成后,我们可以打包该项目,然后在测试工程红进行Maven的引入、测试。
测试
新建Spring 测试工程,引入依赖:
<dependency><groupId>com.xbhog</groupId><artifactId>demo-spring-boot-starter</artifactId><version>1.0</version>
</dependency>
@RestController
public class BasicController implements ApplicationContextAware {private ApplicationContext applicationContext;/**两种引入方式都可以@Autowiredprivate DemoBean demoBean;*/@GetMapping("/configTest")public void configTest() {DemoBean demoBean = applicationContext.getBean(DemoBean.class);demoBean.getDemo();}@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext = applicationContext;}
}
请求地址后,可以观察控制台,如下日志表示SpringBoot Starter可以使用了。

到此,一个简单的Starter开发完成了,后续可以围绕工程,根据需求和业务,对通用功能(接口操作日志、异常、加解密、白名单等)进行封装,最后打到Maven仓库中进行使用。
自定义SpringBoot Starter(全局加解密)
来源
在之前金融系统开发中,需要对接多个第三方的服务且数据安全性要求比较高;在接口评审阶段需要双方在数据传输的时候进行接口加解密;起初在第一个服务对接的时候,将相关的加解密操作写到工具类中;随着后续服务的增多,代码的侵入越来越严重。
封装
选择通过Starter进行功能的封装;好处:引用方便,开发迭代方便,团队复用度高且对业务没有侵入。
开发
思路:通过配置文件初始化,让配置类注解@ComponentScan扫描到的Bean等注入到SpringBoot中,通过自定义注解和``RequestBodyAdvice/ResponseBodyAdvice组合拦截请求,在BeforBodyRead/beforeBodyWrite`中进行数据的前置处理,解密后映射到接口接收的字段或对象。
接口上的操作有两种方式:
- 注解+
AOP实现 - 注解+
RequestBodyAdvice/ResponseBodyAdvice
这里我选择的第二种的RequestBodyAdvice/ResponseBodyAdvice,抛砖引玉一下。
**【注】**第二种存在的局限性是:只能针对POST请求中的Body数据处理,无法针对GET请求进行处理。
项目结构:
encryAdecry-spring-boot-starter
└── src└── main└── java└── com.xbhog├── advice│ ├──ResponseBodyEncryptAdvice.java│ └──RequestBodyDecryptAdvice.java├── annotation│ └──SecuritySupport├── handler│ ├──impl│ │ └──SecurityHandlerImpl.java│ └──SecurityHandler└── holder│ ├──ContextHolder.java│ ├──EncryAdecryHolder.java│ └──SpringContextHolder.java└──GlobalConfig.java└── resources└── META-INF└── spring.factories
项目处理流程图:

核心代码:
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {log.info("进入【RequestBodyDecryptAdvice】beforeBodyRead的操作,方法:{}",parameter.getMethod());SecuritySupport securitySupport = parameter.getMethodAnnotation(SecuritySupport.class);assert securitySupport != null;ContextHolder.setCryptHolder(securitySupport.securityHandler());String original = IOUtils.toString(inputMessage.getBody(), Charset.defaultCharset());//todolog.info("该流水已插入当前请求流水表");String handler = securitySupport.securityHandler();String plainText = original;if(StringUtils.isNotBlank(handler)){SecurityHandler securityHandler = SpringContextHolder.getBean(handler, SecurityHandler.class);plainText = securityHandler.decrypt(original);}return new MappingJacksonInputMessage(IOUtils.toInputStream(plainText, Charset.defaultCharset()), inputMessage.getHeaders());
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {log.info("进入【ResponseBodyEncryptAdvice】beforeBodyWrite的操作,方法:{}",returnType.getMethod());String cryptHandler = ContextHolder.getCryptHandler();SecurityHandler securityHandler = SpringContextHolder.getBean(cryptHandler, SecurityHandler.class);assert body != null;return securityHandler.encrypt(body.toString());
}
该Starter中的全局加解密默认采用的国密非对称加密SM2,在开发过程中遇到了该问题InvalidCipherTextException: invalid cipher text
【原因】 私钥和公钥值不是成对存在的,每次调用SmUtil.sm2()会生成不同的随机密钥对。
【解决】在该Starter中采用@PostConstruct修饰方法,在项目运行中只会初始化运行一次该方法,保证了SmUtil.sm2()只会调用一次,不会生成不同的随机秘钥对。
【ISSUES#1890】详细请看该地址:https://hub.fgit.cf/dromara/hutool/issues/1890
/*** @author xbhog* @date 2024/02/01 13:23**/
@Slf4j
@Component
public class EncryAdecryHolder {public static SM2 sm2 = null;@PostConstructpublic void encryHolder(){KeyPair pair = SecureUtil.generateKeyPair("SM2");byte[] privateKey = pair.getPrivate().getEncoded();byte[] publicKey = pair.getPublic().getEncoded();log.info("生成的公钥:{}",publicKey);log.info("生成的私钥:{}",privateKey);sm2= SmUtil.sm2(privateKey, publicKey);}
}
除了默认的加密方式,还可以通过SecurityHandler接口进行扩展,扩展出来的impl可以在@SecuritySupport(securityHandler="xxxxxx")中指定。
/*** @author xbhog* @describe: 全局加解密注解* @date 2023/6/8*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecuritySupport {/*securityHandlerImpl*/String securityHandler() default "securityHandlerImpl";String exceptionResponse() default "";}
测试
复用之前的测试项目,引用打包的mavne依赖:
<dependency><groupId>com.xbhog</groupId><artifactId>encryAdecry-spring-boot-starter</artifactId><version>1.0.0</version>
</dependency>
启动项目,初始化公私钥。


测试接口代码如下:
@Slf4j
@RestController
public class BasicController implements ApplicationContextAware {@Resource(name = "demoSecurityHandlerImpl")private SecurityHandler encryAdecry;private ApplicationContext applicationContext;// http://127.0.0.1:8080/hello?name=lisi//@SecuritySupport(securityHandler = "demoSecurityHandlerImpl")@SecuritySupport@PostMapping("/hello")public String hello(@RequestBody String name) {return "Hello " + name;}@GetMapping("/configTest")public String configTest(@RequestParam("name") String name) {/*DemoBean demoBean = applicationContext.getBean(DemoBean.class);demoBean.getDemo();*/return encryAdecry.encrypt(name);//return MD5.create().digestHex16(name);}
}

优化
优化后的项目结构:
encryAdecry-spring-boot-starter
└── src└── main└── java└── com.xbhog├── advice│ ├──ResponseBodyEncryptAdvice.java│ └──RequestBodyDecryptAdvice.java├── annotation│ └──SecuritySupport├── handler│ ├──impl│ │ └──EncryAdecryImpl.java│ └──SecurityHandler└── holder│ ├──ContextHolder.java│ └──SpringContextHolder.java├──GlobalProperties.java└──GlobalConfig.java└── resources└── META-INF└── spring.factories
增加配置类,用于绑定外部配置(properties和YAML)到Java对象的的一种机制;
@Data
@ConfigurationProperties(GlobalProperties.PREFIX)
public class GlobalProperties {/*** 默认前缀*/public static final String PREFIX = "encryption.type";/*** 加解密算法*/private String algorithmType;/*** 加解密key值*/private String key;
}
注解修改:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SecuritySupport {/*** 项目默认加解密实现类encryAdecryImpl* */String securityHandler() default "encryAdecryImpl";}
重写Starter默认的加解密方式:
@Slf4j
@Component
public class EncryAdecryImpl implements SecurityHandler {@Resourceprivate GlobalProperties globalProperties;private static volatile SM2 sm2;@Overridepublic String encrypt(String original) {log.info("【starter】具体加密的数据{}",original);return sm2.encryptBase64(original, KeyType.PublicKey);}@Overridepublic String decrypt(String original) {String decryptData = StrUtil.utf8Str(sm2.decryptStr(original, KeyType.PrivateKey));log.info("【starter】具体解密的数据:{}",decryptData);return decryptData;}@PostConstruct@Overridepublic void init() {log.info("======>获取映射的加密算法类型:{}",globalProperties.getAlgorithmType());//传的是加密算法KeyPair pair = SecureUtil.generateKeyPair(globalProperties.getAlgorithmType());byte[] privateKey = pair.getPrivate().getEncoded();byte[] publicKey = pair.getPublic().getEncoded();sm2= SmUtil.sm2(privateKey, publicKey);}
}
相关文章:
【SpringBootStarter】自定义全局加解密组件
【SpringBootStarter】 目的 了解SpringBoot Starter相关概念以及开发流程实现自定义SpringBoot Starter(全局加解密)了解测试流程优化 最终引用的效果: <dependency><groupId>com.xbhog</groupId><artifactId>globalValidation-spring…...
【射影几何15】python双曲几何工具geometry_tools
目录 一、说明二、环境问题:如何安装三、实现一个简单的例子四、绘制双曲组五、使用有限状态自动机加快速度六、资源和代码 一、说明 Geometry_tools 是一个 Python 包,旨在帮助您处理和可视化双曲空间和射影空间上的群动作。 该包主要构建在 numpy、…...
机器人抓取 [ 题目/摘要 ] 更新中..
题目:Robotic Grasping of Novel Objects using Visionl 链接:机器人抓取新物体 | IEEE Xplore(IEEE的Xplore) 【端到端】 摘要:我们考虑抓取新物体的问题,特别是第一次通过视觉看到的物体。抓取以前未知的…...
【51单片机】外部中断和定时器中断
目录 中断系统中断介绍中断概念 中断结构及相关寄存器中断结构中断相关寄存器 外部中断实验外部中断配置软件设计实验现象 定时器中断定时器介绍51 单片机定时器原理51 单片机定时/计数器结构51 单片机定时/计数器的工作方式 定时器配置硬件设计软件设计实验现象 中断系统 本章…...
零售行业供应商数据分发,怎样提高安全性和效率?
零售行业是我国经济发展的重要组成,零售行业包罗万象,如包括汽车零售、日化零售、快消品零售等,不同细分行业的运营模式各不相同,但大体来说,零售行业都具备最基础的供应商和零售商,供应商将商品或服务卖给…...
Windows下Node.js下载安装及环境变量配置教程
Windows下Node.js下载安装及环境变量配置教程 安装版本:node-v18.19.0-x64.msi 文章目录 Windows下Node.js下载安装及环境变量配置教程一、Node.js和NPM简介二、下载地址三、安装步骤四、环境配置五、安装淘宝镜像总结 一、Node.js和NPM简介 1、Node.js …...
广义表-C语言
广义表(Generalized List)是一种扩展了线性表的数据结构,它在线性表的基础上增加了元素可以是表的特点。在广义表中,元素不仅可以是单个的数据元素,还可以是一个子表,而子表中的元素也可以是数据元素或其他…...
uniapp+uView 【详解】录音,自制音频播放器
效果预览 代码实现 <template><view class"btnListBox"><view class"audioBox" v-if"audioLength"><u-row><u-col span"2"><u--text aligncenter :text"currentTime"></u--text>…...
机器学习---概率图模型(隐马尔可夫模型、马尔可夫随机场、条件随机场)
1. 隐马尔可夫模型 机器学习最重要的任务是根据已观察到的证据(例如训练样本)对感兴趣的未知变量(例如类别标 记)进行估计和推测。概率模型(probabilistic model)提供了一种描述框架,将描述任…...
cool 框架 node 后端封装三方Api post请求函数
1.需求 现在一些数据源 ,需要从三方地址拿到一些数据 比如说电影列表 信息了 影院列表信息了 等一些展示的数据,但是人家这种东西 害需要使用 appkey appserect 这种验签 这种需求 你前端调用接口是直接调用不了的 因为需要用到验签 需要后端接口转接一…...
awd总结
总结: 由于是第一次参加AWD比赛,各方面经验都不足,在参赛的前几天也是疯狂搜集各种脚本、框架、工具等,同时也参考b站的视频进行学习,我发现就是还是实操才能更快的学习 我觉得就是我前期的准备工作不足,…...
【react】react+es6+antd5.13.2+ts,antd表格的操作如何在父组件写?
reactes6antd5.13.2ts,antd表格的操作如何在父组件写? 我的子组件columns.tsx,只加表头,操作放在父组件。 columns.tsx的代码: export const dataColumns [{title: 项目成员,dataIndex: name,key: name,},{title: 可选账号,alig…...
virtio笔记
最近在看虚拟化相关的东西,以virtio-console为例,记录下。 此文只是学习笔记,文中肯定有不少错误,不要参考 devicemd侧: virtio_console.c中,初始化会对port->cb赋值为 viritio_console_control_tx&am…...
初始web服务器(并基于idea来实现无需下载的tomcat)
前言 前面学习了对应的http协议,我们知道了他是在网络层进行数据传输的协议,负责相应数据以及接收数据的规则,但是在人员开发后端的时候不仅仅需要你写io流进行数据传输,还需要你进行对应的tcp协议来进行数据打包发送http协议-CSD…...
软件文档测试
1 文档测试的范围 软件产品由可运行的程序、数据和文档组成。文档是软件的一个重要组成部分。 在软件的整人生命周期中,会用到许多文档,在各个阶段中以文档作为前阶段工作成果的体现和后阶段工作的依据。 软件文档的分类结构图如下图所示: …...
从零开始手写mmo游戏从框架到爆炸(七)— 消息封装
导航:从零开始手写mmo游戏从框架到爆炸(零)—— 导航-CSDN博客 上一篇,我们初步把消息handler 注册到了服务中,在进行后续工作之前我们需要再做一些准备工作。 第一:把之前自己管理的bean放到spring中…...
从Unity到Three.js(画线组件line)
JavaScript 0基础,只是照着官方文档临摹了下,之后有时间再进行细节学习和功能封装。 import * as THREE from three; //引入threejsconst renderer new THREE.WebGLRenderer();//创建渲染器 //设置渲染范围,当前撑满全屏,屏幕左上角是&…...
LCP 30. 魔塔游戏 - 力扣(LeetCode)
题目描述 小扣当前位于魔塔游戏第一层,共有 N 个房间,编号为 0 ~ N-1。每个房间的补血道具/怪物对于血量影响记于数组 nums,其中正数表示道具补血数值,即血量增加对应数值;负数表示怪物造成伤害值,即血量减…...
数据结构——单向链表和双向链表的实现(C语言版)
目录 前言 1. 链表 1.1 链表的概念及结构 1.2 链表的分类 2. 单链表接口实现 2.1 数据结构设计与接口函数声明 2.2 创建结点,打印,查找 2.3 尾插,头插,尾删,头删 2.4 插入或删除 2.4.1在指定位置后 2.4.2在…...
TCP和UDP相关问题(重点)(4)——4.使用TCP的协议有哪些?使用UDP的协议有哪些?
4.使用TCP的协议有哪些?使用UDP的协议有哪些? 使用TCP的协议有:HTTP3.0之前的HTTP协议、HTTPS、FTP、SMTP、SSH... 使用UDP的协议有:HTTP3.0、DNS、DHCP......
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
React第五十七节 Router中RouterProvider使用详解及注意事项
前言 在 React Router v6.4 中,RouterProvider 是一个核心组件,用于提供基于数据路由(data routers)的新型路由方案。 它替代了传统的 <BrowserRouter>,支持更强大的数据加载和操作功能(如 loader 和…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
C++.OpenGL (10/64)基础光照(Basic Lighting)
基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...
Leetcode33( 搜索旋转排序数组)
题目表述 整数数组 nums 按升序排列,数组中的值 互不相同 。 在传递给函数之前,nums 在预先未知的某个下标 k(0 < k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
文章目录 1. 题目描述1.1 链表节点定义 2. 理解题目2.1 问题可视化2.2 核心挑战 3. 解法一:HashSet 标记访问法3.1 算法思路3.2 Java代码实现3.3 详细执行过程演示3.4 执行结果示例3.5 复杂度分析3.6 优缺点分析 4. 解法二:Floyd 快慢指针法(…...
怎么开发一个网络协议模块(C语言框架)之(六) ——通用对象池总结(核心)
+---------------------------+ | operEntryTbl[] | ← 操作对象池 (对象数组) +---------------------------+ | 0 | 1 | 2 | ... | N-1 | +---------------------------+↓ 初始化时全部加入 +------------------------+ +-------------------------+ | …...
TJCTF 2025
还以为是天津的。这个比较容易,虽然绕了点弯,可还是把CP AK了,不过我会的别人也会,还是没啥名次。记录一下吧。 Crypto bacon-bits with open(flag.txt) as f: flag f.read().strip() with open(text.txt) as t: text t.read…...
