Spring API 接口加密/解密
API 接口加密/解密
为了安全性需要对接口的数据进行加密处理,不能明文暴露数据。为此应该对接口进行加密/解密处理,对于接口的行为,分别有:
- 入参,对传过来的加密参数解密。接口处理客户端提交的参数时候,这里统一约定对 HTTP Raw Body 提交的数据(已加密的密文),转换为 JSON 处理,这是最常见的提交方式。其他 QueryString、标准 Form、HTTP Header 的入参则不支持。
- 出参,对返回值进行加密。接口统一返回加密后的 JSON 结果。
有人把加密结果原文输出,如下图所示:

但笔者觉得那是一种反模式,而保留原有 JSON 结构更好,如下提交的 JSON。
{"errCode": "0","data": "BQduoGH4PI+6jxgu+6S2FWu5c/vHd+041ITnCH9JulUKpPX8BvRTvBNYfP7……"
}
另外也符合既有的统一返回结果,即把data数据加密,其他code、msg等的正常显示。
系统要求:只支持 Spring + Jackson 的方案。
加密算法
加密算法需要调用方(如浏览器)与 API 接口协商好。一般采用 RSA 加密算法。虽然 RSA 没 AES 速度高,但胜在是非对称加密,AES 这种对称加密机制在这场合就不适用了(因为浏览器是不能放置任何密钥的,——除非放置非对称的公钥)。
当然,如果你设计的 API 接口给其他第三方调用而不是浏览器,可以保证密钥安全的话,那么使用 AES 也可以,包括其他摘要算法同理亦可,大家商定好算法(md5/sha1/sha256……)和盐值(Slat)即可。
该组件当前仅支持 RSA(1024bit key)。下面更多的算法在路上。
- RSA(512/2048……)
- AES
- MD5/SHA1/SHA256…… with Slat
使用方式
初始化
在 YAML 配置中加入:
api:EncryptedBody:publicKey: MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCmkKluNutOWGmAK2U……privateKey: MIICdgIBADANBgkqhkiG9w0BAQ……
主要是 RSA 的公钥/私钥。然后在 Spring 配置类WebMvcConfigurer中加入:
@Value("${api.EncryptedBody.publicKey}")
private String apiPublicKey;@Value("${api.EncryptedBody.privateKey}")
private String apiPrivateKey;@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {converters.add(0, new EncryptedBodyConverter(apiPublicKey, apiPrivateKey));
}
配置要加密的数据
使用方式很简单,其实就是添加一个 Java 注解@EncryptedData到你的 Java Bean 上即可。
不过我们还是按照正儿八经的循序渐进的方式去看看。首先是解密请求的数据,我们观察这个 Spring MVC 接口声明,与一般的 JSON 提交数据方式无异,添加了注解@RequestBody,其他无须修改:
@PostMapping("/submit")
boolean jsonSubmit(@RequestBody User user);
重点是 User 这个 DTO,为了标明是加密数据,需要在这个 Bean 上声明我们自定义的注解@EncryptedData:
package com.ajaxjs.api.encryptedbody;@EncryptedData
public class User {private String name;private int age;// Getters and Setters
}
同时我们提交的对象不再是 User 的 JSON,而是DecodeDTO(虽然最终转换为User,成功解密的话),即:
package com.ajaxjs.api.encryptedbody;import lombok.Data;@Data
public class DecodeDTO {/*** Encrypted data*/private String data;
}
当然你可以修改这个 DTO 为你符合的结构。提交的样子就是像:
{"data": "BQduoGH4PI+6jxgu+6S2FWu5c/vHd+041ITnCH9JulUKpPX8BvRTvBNYfP7……"
}
这个加密过的密文怎么来的?当然是你客户端加密后的结果。或者从下面小节说的方式,返回一段密文。
返回加密的数据
下面 Controller 方法返回一个 User 对象,没有任何修改。
@GetMapping("/user")
User User();……@Override
public User User() {User user = new User();user.setAge(1);user.setName("tom");return user;
}
我们同样需要加一个注解@EncryptedData即可对其加密。当前版本中暂不支持字段级别的加密,只支持整个对象加密。
返回结果如下:
{"status": 1,"errorCode": null,"message": "操作成功","data": "ReSSPC34JE+O/SmLCxE5zVJb6D2tzp1f5pfQyKdjvOWkQQ+qDjcjw/2m/KPA+2+uc9kseqFryXNPIZCEfsaOCJAqzMtrXyZ0JPB1skeJxKOngS5USijsY0UZqN9hLS3O/7CBLlSGkEuyXZV//WcWDG9BpQ4TAKrlRfwM4bnCo+E="
}
添加依赖
哦~对了,别忘了添加依赖,——没单独搞 jar 包,直接 copy 代码吧~才三个类:
源码。
其中ResponseResultWrapper就是统一返回结果的类,你可以改为你项目的,——其他的没啥依赖了,——还有就是 RSA 依赖我的工具包:
<dependency><groupId>com.ajaxjs</groupId><artifactId>ajaxjs-util</artifactId><version>1.1.8</version>
</dependency>
很小巧的,才60kb 的 jar 包——请放心食用~
实现方式
这里说说实现原理,以及一些 API 设计风格的思考。
我们这种的用法,相当于接收了 A 对象(加密的,DecodeDTO),转换为 B 对象(解密的,供控制器使用)。最简单的方式就是这样的:
@PostMapping("/submit")
boolean jsonSubmit(@RequestBody DecodeDTO dto) {User user = 转换函数(dto.getData());
}
但是这种方法,方法数量一多则遍地DecodeDTO,API 文档也没法写了(破坏了代码清晰度,不能反映原来代码的意图)。为此我们应该尽量采用“非入侵”的方法,所谓非入侵,就是不修改原有的代码,只做额外的“装饰”。这种手段有很多,典型如 AOP,其他同类的开源库sa-encrypt-body-spring-boot、encrypt-body-spring-boot-starter也是不约而同地使用 AOP。
然而笔者个人来说不太喜欢 AOP,可能也是不够熟悉吧——反正能不用则不用。如果不用 AOP 那应该如何做呢?笔者思考了几种方式例如 Filter、拦截器等,但最终把这个问题定位于 JSON 序列化/反序列化层面上,在执行这一步骤之前就可以做加密/解密操作了。开始以为可以修改 Jackson 全局序列化方式,但碍于全局的话感觉不太合理,更合适的是在介乎于 Spring 与 Jackson 结合的地方做修改。于是有了在的MappingJackson2HttpMessageConverter基础上扩展的 EncryptedBodyConverter,重写了read方法,在反序列化之前先做解密操作,writeInternal方法亦然。
核心方法就一个类,不足一百行代码:
import com.ajaxjs.springboot.ResponseResultWrapper;
import com.ajaxjs.util.EncodeTools;
import com.ajaxjs.util.cryptography.RsaCrypto;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;import java.io.IOException;
import java.lang.reflect.Type;public class EncryptedBodyConverter extends MappingJackson2HttpMessageConverter {public EncryptedBodyConverter(String publicKey, String privateKey) {super();this.publicKey = publicKey;this.privateKey = privateKey;}private final String publicKey;private final String privateKey;/*** 使用私钥解密字符串** @param encryptBody 经过 Base64 编码的加密字符串* @param privateKey 私钥字符串,用于解密* @return 解密后的字符串*/static String decrypt(String encryptBody, String privateKey) {byte[] data = EncodeTools.base64Decode(encryptBody);return new String(RsaCrypto.decryptByPrivateKey(data, privateKey));}/*** 使用公钥加密字符串* <p>* 该方法采用RSA加密算法,使用给定的公钥对一段字符串进行加密* 加密后的字节数组被转换为 Base64 编码的字符串,以便于传输和存储** @param body 需要加密的原始字符串* @param publicKey 用于加密的公钥字符串* @return 加密后的 Base64 编码字符串*/static String encrypt(String body, String publicKey) {byte[] encWord = RsaCrypto.encryptByPublicKey(body.getBytes(), publicKey);return EncodeTools.base64EncodeToString(encWord);}/*** 重写 read 方法以支持加密数据的读取** @param type 数据类型,用于确定返回对象的类型* @param contextClass 上下文类,未在本方法中使用* @param inputMessage 包含加密数据的 HTTP 输入消息* @return 根据类型参数反序列化后的对象实例* @throws IOException 如果读取或解析过程中发生 I/O 错误* @throws HttpMessageNotReadableException 如果消息无法解析为对象实例*/@Overridepublic Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {Class<?> clz = (Class<?>) type;if (clz.getAnnotation(EncryptedData.class) != null) {ObjectMapper objectMapper = getObjectMapper();DecodeDTO decodeDTO = objectMapper.readValue(inputMessage.getBody(), DecodeDTO.class);String encryptBody = decodeDTO.getData();String decodeJson = decrypt(encryptBody, privateKey);return objectMapper.readValue(decodeJson, clz);}return super.read(type, contextClass, inputMessage);}@Overrideprotected void writeInternal(Object object, Type type, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {Class<?> clz = (Class<?>) type;if (object instanceof ResponseResultWrapper && clz.getAnnotation(EncryptedData.class) != null) {ResponseResultWrapper response = (ResponseResultWrapper) object;Object data = response.getData();String json = getObjectMapper().writeValueAsString(data);String encryptBody = encrypt(json, publicKey);response.setData(encryptBody);}super.writeInternal(object, type, outputMessage);}
}
相关文章:
Spring API 接口加密/解密
API 接口加密/解密 为了安全性需要对接口的数据进行加密处理,不能明文暴露数据。为此应该对接口进行加密/解密处理,对于接口的行为,分别有: 入参,对传过来的加密参数解密。接口处理客户端提交的参数时候,…...
漏洞扫描:网络安全的 “体检” 与 “防护指南”
在当今数字化时代,网络安全如同守护城堡的坚固城墙,而漏洞扫描则是检查城墙是否存在缝隙与薄弱环节的重要手段。那么,究竟什么是漏洞扫描?又该如何进行呢? 什么是漏洞扫描? 漏洞扫描是一种安全检测过程&a…...
【可靠有效】springboot使用netty搭建TCP服务器
Netty Netty是一个高性能、异步事件驱动的网络应用程序框架,它提供了对并发和异步编程的抽象,使得开发网络应用程序变得更加简单和高效。 在Netty中,EventLoopGroup是处理I/O操作的多线程事件循环器。在上面的示例中,我们创建了两个EventLoopGroup实例:bossGroup和worker…...
机器视觉中的单线程、多线程与跨线程:原理与应用解析
在机器视觉应用中,程序的运行效率直接影响到系统的实时性和稳定性。随着任务复杂度的提高,单线程处理往往无法满足高性能需求,多线程技术因此被广泛应用。此外,跨线程操作(如在多线程中更新界面或共享资源)…...
0040__Linux内核4.14版本——drm框架分析(1)——drm简介
https://download.csdn.net/blog/column/11175480/133602965 通过DRM绘制图像_drmmodegetresources-CSDN博客 https://zhuanlan.zhihu.com/p/336395524 19. 屏幕显示(DRM)介绍 — [野火]Linux基础与应用开发实战指南——基于LubanCat-RK系列板卡 文档 DRM设备信息_drmmoder…...
珞珈一号夜光遥感数据地理配准,栅格数据地理配准
目录 一、夜光数据下载: 二、夜光遥感数据地理配准 三、计算夜光数据值 四、辐射定标 五、以表格显示分区统计 五、结果验证 夜光数据位置和路网位置不匹配,虽然都是WGS84坐标系,不匹配!!!不要看到就直接…...
【GlobalMapper精品教程】091:根据指定字段融合图斑(字段值相同融合到一起)
文章目录 一、加载数据二、符号化三、融合图斑1. 根据图斑位置进行融合2. 根据指定字段四、注意事项一、加载数据 订阅专栏后,从私信中查收配套实验数据包,找到data091.rar,解压并加载,如下图所示: 属性表如下: 二、符号化 为了便于比对不同的融合结果,查看属性表根据…...
Quartz任务调度框架实现任务动态执行
说明:之前使用Quartz,都是写好Job,指定一个时间点,到点执行。最近有个需求,需要根据前端用户设置的时间点去执行,也就是说任务执行的时间点是动态变化的。本文介绍如何用Quartz任务调度框架实现任务动态执行…...
ESP-IDF学习记录(1)ESPIDF环境安装,框架了解,资料整理
以后只要有空就会进行学习记录,主要是自用,学到哪记录到哪,有时候东西记录下来能得到不通的理解。 最终的目的是为了用esp32驱动屏幕,学习设计LVGL界面,做一些小产品,有益于公司及个人。之前接触多的UI还是…...
Windows系统提示synsoacc.dll文件报错要怎么解决?
一、文件丢失问题:深度剖析与应对策略 文件丢失是电脑运行时常见的问题之一。它可能由多种原因引起,如硬盘故障、病毒攻击、不当的文件操作等。当Windows系统提示synsoacc.dll丢失时,通常意味着该文件对于当前正在运行的程序或系统服务至关重…...
React(一)—— router/useRef/useState
文章目录 项目地址一、构建项目1.1 使用vite构建项目1.2 所需插件二、Router2.1 安装router2.2 创建路由规则2.3 创建导航栏2.3.1 添加样式文件2.3.2 添加导航栏组件2.3.3 给每个页面添加Menu导航栏2.4 通过路由给页面传值三、Hooks3.1 useRef3.2 useRef操作DOM元素3.3 useRef进…...
ipad如何直连主机(Moonlight Sunshine)
Windows 被连接主机(Windows) 要使用的话需要固定ip,不然ip会换来换去,固定ip方法本人博客有记载Github下载Sunshine Sunshine下载地址除了安装路径需要改一下,其他一路点安装完成后会打开Sunshine的Web UIÿ…...
音视频入门知识(二)、图像篇
⭐二、图像篇 视频基本要素:宽、高、帧率、编码方式、码率、分辨率 其中码率的计算:码率(kbps)=文件大小(KB)*8/时间(秒),即码率和视频文件大小成正比 YUV和RGB可相互转换 ★YUV(原始数据&am…...
v-if 和 v-show 的区别
一、原理区别 1. v-if 这是一个指令,用于条件性地渲染一个元素块。当v-if表达式的值为true时,元素及其包含的子元素才会被渲染到 DOM 中;当表达式的值为false时,元素及其子元素会被完全移除。这意味着在切换v-if的条件时&#x…...
解密MQTT协议:从QOS到消息传递的全方位解析
1、QoS介绍 1.1、QoS简介 使用MQTT协议的设备大部分都是运行在网络受限的环境下,而只依靠底层的TCP传输协议,并不 能完全保证消息的可靠到达。 MQTT提供了QoS机制,其核心是设计了多种消息交互机制来提供不同的服务质量,来满足…...
Java-02 深入浅出 MyBatis - MyBatis 快速入门(无 Spring) POM Mapper 核心文件 增删改查
点一下关注吧!!!非常感谢!!持续更新!!! 大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html 目前已经更新到了: MyBatisÿ…...
Unity功能模块一对话系统(4)实现个性文本标签
本期我们将了解如何在TMPro中自定义我们的标签样式,并实现两种有趣的效果。 一.需求描述 1.定义<float>格式的标签,实现标签处延迟打印功能 2.定义<r" "></r>格式的标签,实现标签区间内文本片段的注释显示功能…...
git在idea中操作频繁出现让输入token或用户密码,可以使用凭证助手(使用git命令时输入的用户密码即可) use credential helper
1、打开 idea 设置,找到 git 路径 File | Settings | Version Control | Git 2、勾选 Use credential helper 即可...
毫米波雷达技术:(九)快时间窗和慢时间窗的概念
(一)快时间窗: 快时间窗通常指的是在雷达脉冲周期内,对每个脉冲回波进行采样的时间段。这个时间段非常短,通常在 0 − 100 n s 0-100ns 0−100ns 。在快时间窗内,雷达系统会对接收到的回波信号进行高分辨…...
宠物行业的出路:在爱与陪伴中寻找增长新机遇
在当下的消费市场中,如果说有什么领域能够逆势而上,宠物行业无疑是一个亮点。当人们越来越注重生活品质和精神寄托时,宠物成为了许多人的重要伴侣。它们不仅仅是家庭的一员,更是情感的寄托和生活的调剂。然而,随着行业…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
Map相关知识
数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
虚拟电厂发展三大趋势:市场化、技术主导、车网互联
市场化:从政策驱动到多元盈利 政策全面赋能 2025年4月,国家发改委、能源局发布《关于加快推进虚拟电厂发展的指导意见》,首次明确虚拟电厂为“独立市场主体”,提出硬性目标:2027年全国调节能力≥2000万千瓦࿰…...
【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)
LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 题目描述解题思路Java代码 题目描述 题目链接:LeetCode 3309. 连接二进制表示可形成的最大数值(中等) 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...
Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
wpf在image控件上快速显示内存图像
wpf在image控件上快速显示内存图像https://www.cnblogs.com/haodafeng/p/10431387.html 如果你在寻找能够快速在image控件刷新大图像(比如分辨率3000*3000的图像)的办法,尤其是想把内存中的裸数据(只有图像的数据,不包…...
