Springboot 框架中加解密字段后存储数据库
为防止数据库泄露,表里的敏感字段被曝光,需要对用户的重要数据做加密存取。
选择加密算法: 首先,你需要选择适合你的需求的加密算法。一些常见的加密算法包括AES、RSA、SHA等。具体的选择取决于你要加密的数据和安全需求。
引入加密库: 在你的Spring Boot项目中,引入适用于你所选加密算法的库。例如,如果你选择使用AES加密,可以引入Java的javax.crypto库。
创建加密服务: 创建一个用于加密和解密的服务类。这个服务类应该包含加密和解密方法,并将其注入到Spring容器中以供使用。
定义数据库实体类: 在数据库实体类中,将需要加密的字段定义为加密前的原始字段。在将数据保存到数据库之前,使用加密服务对这些字段进行加密。
加密和解密数据: 在业务逻辑中,调用加密服务对需要加密的数据进行加密,并在需要时进行解密。确保加密密钥的安全存储,不要将密钥硬编码在代码中。
定义接口文件--------------------------------------------------
DecryptField.java :
package xxx.xxx
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.ElementType;
//解密字段注解
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface DecryptField {
}
EncryptField .java :
//解密字段注解
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptField {
}
NeedDecrypt .java :
//作用于对返回值进行解密处理的方法上
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedDecrypt {
}
NeedEncrypt .java :
//作用于对参数进行加密处理的方法上
@Target(value = {ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedEncrypt {
}
AesSupport.java 加密解密算法------------------------------------------
public class AesSupport{
private static final String KEY_ALGORITHM = "AES";
private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";
private static final String AES_KEY = "you aes key"; // 16/24/32-character secret key
private String password;
private SecretKeySpec secretKeySpec;
private static AesSupport instance;
// 私有构造函数,防止外部创建实例
private AesSupport() {
}
// 提供获取单例实例的方法,使用双重检查锁定保证线程安全
public static AesSupport getInstance() throws NoSuchAlgorithmException {
if (instance == null) {
synchronized (AesSupport.class) {
if (instance == null) {
instance = new AesSupport(AES_KEY);
}
}
}
return instance;
}
public AesSupport(String password) throws NoSuchAlgorithmException {
if(StringUtils.isEmpty(password)){
throw new IllegalArgumentException("password should not be null!");
}
this.password = password;
this.secretKeySpec = getSecretKey(password);
}
public String encrypt(String value){
try{
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
byte[] content = value.getBytes("UTF-8");
byte[] encryptData = cipher.doFinal(content);
return Hex.bytesToHexString(encryptData);
}catch (Exception e){
throw new IllegalStateException("AES encrypt "+e.getMessage(),e);
}
}
public String decrypt(String value){
if (StringUtils.isEmpty(value)) {
return "";
}
try {
byte[] encryptData = Hex.hexStringToBytes(value);
Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
byte[] content = cipher.doFinal(encryptData);
return new String(content, "UTF-8");
}catch (Exception e){
throw new IllegalStateException("AES decrypt "+e.getMessage(),e);
}
}
private SecretKeySpec getSecretKey(final String password) throws NoSuchAlgorithmException{
KeyGenerator kg = KeyGenerator.getInstance(KEY_ALGORITHM);
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
random.setSeed(password.getBytes());
kg.init(128, random);
SecretKey secretKey = kg.generateKey();
return new SecretKeySpec(secretKey.getEncoded(), KEY_ALGORITHM);
}
}
DecryptAop.java 解密Aop文件:
@Component
@Aspect
public class DecryptAop {
/**
* 定义需要解密的切入点
*/
@Pointcut(value = "@annotation(xxx..service.crypt.NeedDecrypt)")
public void pointcut() {
}
/**
* 命中的切入点时的环绕通知
*
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//执行目标方法
Object result = proceedingJoinPoint.proceed();
//判断目标方法的返回值类型
if (result instanceof List) {
for (Object tmp : ((List) result)) {
//数据脱敏处理逻辑
this.deepProcess(tmp);
}
} else {
this.deepProcess(result);
}
//log.info("//环绕通知 end");
return result;
}
public void deepProcess(Object obj) throws IllegalAccessException {
if (obj != null) {
//取出输出对象的所有字段属性,并遍历
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
//判断字段属性上是否标记DecryptField注解
if (declaredField.isAnnotationPresent(DecryptField.class)) {
//如果判断结果为真,则取出字段属性数据进行解密处理
declaredField.setAccessible(true);
Object valObj = declaredField.get(obj);
if (valObj != null) {
String value = valObj.toString();
//加密数据的解密处理
try {
value = AesSupport.getInstance().decrypt(value);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DecryptField annotation = declaredField.getAnnotation(DecryptField.class);
//boolean open = annotation.open();
//把解密后的数据重新赋值
declaredField.set(obj, value);
}
}
}
}
}
}
EncryptAop.java 加密Aop文件:
@Component
@Aspect
public class EncryptAop {
/**
* 定义加密切入点
*/
@Pointcut(value = "@annotation(xxx.service.crypt.NeedEncrypt)")
public void pointcut() {
}
/**
* 命中加密切入点的环绕通知
*
* @param proceedingJoinPoint
* @return
* @throws Throwable
*/
@Around("pointcut()")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取命中目标方法的入参数
Object[] args = proceedingJoinPoint.getArgs();
if (args.length > 0) {
for (Object arg : args) {
//按参数的类型进行判断,如果业务中还有其他的类型,可酌情增加
if (arg != null) {
if (arg instanceof List) {
for (Object tmp : ((List) arg)) {
//加密处理
this.deepProcess(tmp);
}
} else {
this.deepProcess(arg);
}
}
}
}
//对敏感数据加密后执行目标方法
Object result = proceedingJoinPoint.proceed();
return result;
}
public void deepProcess(Object obj) throws IllegalAccessException {
if (obj != null) {
//获取对象的所有字段属性并遍历
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
//判断字段属性上是否标记了@EncryptField注解
if (declaredField.isAnnotationPresent(EncryptField.class)) {
//如果判断结果为真,则取出字段属性值,进行加密、重新赋值
declaredField.setAccessible(true);
Object valObj = declaredField.get(obj);
if (valObj != null) {
String value = valObj.toString();
//开始敏感字段属性值加密
String decrypt;
try {
decrypt = AesSupport.getInstance().encrypt(value);
//把加密后的字段属性值重新赋值
declaredField.set(obj, decrypt);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
}
}
通过Aop切入拦截标注了NeedEncrypt 、NeedDecrypt的接口函数,获取到参数字段判断是否标注了DecryptField、EncryptField,对标注的进行加解密操作,最后存储到数据库。
在Bean对象类进行字段标注:
public class User{
private String id;
@EncryptField
@DecryptField
private String name;
@EncryptField
@DecryptField
private String phonenum;
}
在接口方法上进行方法标注:
@NeedEncrypt
public int updateUser(User user) throws Exception {
}
@NeedDecrypt
public List<User> queryUser(String userId) throws Exception {
}
注意不要在接口调用路径上进行重复标注,否则会加密两次,最后导致解密不出来。
相关文章:
Springboot 框架中加解密字段后存储数据库
为防止数据库泄露,表里的敏感字段被曝光,需要对用户的重要数据做加密存取。 选择加密算法: 首先,你需要选择适合你的需求的加密算法。一些常见的加密算法包括AES、RSA、SHA等。具体的选择取决于你要加密的数据和安全需求。 引入…...
计算机毕设 大数据工作岗位数据分析与可视化 - python flask
文章目录 0 前言1 课题背景2 实现效果3 项目实现3.1 概括 3.2 Flask实现3.3 HTML页面交互及Jinja2 4 **完整代码**5 最后 0 前言 🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要…...
Maven聚合项目配合Springcloud案例
创建maven项目 导入依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache…...
目标检测网络系列——YOLO V1
文章目录 One Stage DectectionYOLO网络正向预测pipline反向传播过程理解grid和grid对应的B个预测框YOLO网络的限制对比实验与其他的real-time detection的对比VOC 2007数据集的错误分析YOLO和Fast RCNN的集成学习VOC 2012数据集结果YOLO模型的泛化性DEMOOne Stage Dectection …...
任务工单发送失败重试方案设计
需求背景: 该系统为一个工单系统,其中任务工单为该系统中的一个模块;任务工单它是需要周期性调度的一种任务类型;可以按照用户配置的时间周期定时性触发的。由于任务需要发送到对应的工作人员上,所以这里需要先对员工进…...
关于 Vue-iClient-MapboxGL 的使用注意事项
官网:https://iclient.supermap.io/web/apis/vue/zh/api/guide/installation.html 关于图的使用,其余的引入步骤不再赘述,仅说注意事项。 推荐使用的是全局引入,也就是完整引入 因为单独引入我踩了不少坑,比如说 cs…...
Go 语言 map 如何顺序读取?
Go 语言中的 map 是一种非常强大的数据结构,它允许我们快速地存储和检索键值对。 然而,当我们遍历 map 时,会有一个有趣的现象,那就是输出的键值对顺序是不确定的。 现象 先看一段代码示例: package mainimport &q…...
flutter StreamSubscription 订阅者 stream
当您使用[Stream.listen]收听[Stream]时 则返回[StreamSubscription]对象 List<StreamSubscription?> subscriptions []; overridevoid initState() {super.initState();//subscriptions列表添加两个StreamSubscription。Stream.listen返回StreamSubscription对象subs…...
安全性算法
目录 一、安全性算法 二、基础术语 三、对称加密与非对称加密 四、数字签名 五、 哈希算法 六、哈希算法碰撞与溢出处理 一、安全性算法 安全性算法的必要性: 安全性算法的必要性是因为在现代数字化社会中,我们经常需要传输、存储和处理敏感的数据…...
解决ASP.NET Core的中间件无法读取Response.Body的问题
概要 本文主要介绍如何在ASP.NET Core的中间件中,读取Response.Body的方法,以便于我们实现更多的定制化开发。本文介绍的方法适用于.Net 3.1 和 .Net 6。 代码和实现 现象解释 首先我们尝试在自定义中间件中直接读取Response.Body,代码如…...
DownloadingImages 下载缓存图片,显示图片文字列表
1. 用到的技术点: 1) Codable : 可编/解码 JSON 数据 2) background threads : 后台线程 3) weak self : 弱引用 4) Combine : 取消器/组合操作 5) Publishers and Subscribers : 发布者与订阅者 6) FileManager : 文件管理器 7) NSCache : 缓存 2. 网址: 2.1 测试接口网址: …...
【应用层协议】HTTPS的加密流程
目录 一、认识HTTPS 二、密文 1、对称加密 2、非对称加密 三、HTTPS加密流程 1、建立连接 2、证书验证 3、密钥协商 4、数据传输 5、关闭连接 总结 在数字化时代,互联网已经成为我们生活和工作中不可或缺的一部分。然而,随着数据的不断增加&a…...
最新AI创作系统/AI绘画系统/ChatGPT系统+H5源码+微信公众号版+支持Prompt应用
一、AI创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的AI智能问答系统和AI绘画系统。本期针对源码系统整体测试下来非常完美,可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图…...
Z410 2023款无人机,专为零基础开发者打造的入门级开源无人机
为什么开发Z410升级款-Easydrone无人机 新手开发者通常在本科阶段加入人工智能行业,对无人机二次开发往往一知半解,面临着C、Python、ROS和mavlink等一系列入门知识,学习起来非常困难,学习的过程中也面临许多挫折。为了帮助零基础…...
elementui修改message消息提示颜色
/* el弹出框样式 */ .el-message {top: 80px !important;border: 0; }.el-message * {color: var(--white) !important;font-weight: 600; }.el-message--success {background: var(--themeBackground); }.el-message--warning {background: var(--gradientBG); }.el-message--…...
Linux和Hadoop的学习
目录 1. Linux的常用快捷键2. Hadoop集群部署问题汇总 1. Linux的常用快捷键 复制:CtrlshiftC 粘贴:CtrlshiftV TAB:补全命令 编写输入:i 退出编写:esc 保存并退出:shift: 2. Hadoop集群部署问…...
通达信指标预警信号,自动发送给微信好友1.0
1.功能介绍:十一节假日期间写了一个,可将股票指标预警信号,自动发送给微信好友/微信群(即电脑端的消息,通过模拟微信操作可在手机上显示)。本工具按通达信写的,如果大智慧,同花顺也能…...
浅谈CDN内容分发与全局负载均衡
CDN简介 CDN的全称是Content Delivery Network,即内容分发网络。CDN是构建在现有网络基础之上的智能虚拟网络,依靠部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,…...
【框架风格】解释器模式
1、描述 解释器框架风格(Interpreter Framework Style)是一种软件架构风格,其核心思想是构建一个解释器(Interpreter)来解释并执行特定领域或问题领域的语言或规则。以下是解释器框架风格的一些特点: 1. 领…...
c++视觉图像线性混合
图像线性混合 使用 cv::addWeighted() 函数对两幅图像进行线性混合。alpha 和 beta 是两幅图像的权重,它们之和应该等于1。gamma 是一个可选的增益,这里设置为0。 你可以通过调整 alpha 的值来改变混合比例。如果 alpha0.5,则两幅图像等权重…...
别再只开会了!解锁Jitsi隐藏玩法:用Freeswitch+Jigasi打造智能电话会议IVR
解锁Jitsi企业级应用:用FreeswitchJigasi构建智能会议IVR系统 当视频会议成为企业刚需,大多数团队仍停留在基础会议功能层面。开源工具Jitsi与电信级软交换平台Freeswitch的结合,能创造出远超常规会议体验的智能交互系统。想象一下这样的场景…...
OpenClaw异常处理手册:百川2-13B任务失败排查全攻略
OpenClaw异常处理手册:百川2-13B任务失败排查全攻略 1. 为什么需要这份手册 上周我尝试用OpenClaw百川2-13B模型自动处理日报生成任务时,连续三天凌晨任务失败。每次起床看到控制台的红色错误提示,都要花半小时翻日志找原因。最崩溃的是&am…...
SELF-REFINE in Action: Enhancing LLM Outputs Through Iterative Self-Feedback
1. 什么是SELF-REFINE?为什么LLM需要自我迭代? 想象一下你正在写一封重要邮件。第一稿可能直接了当但缺乏礼貌,经过几次修改后,措辞变得更加得体。这就是人类通过自我反馈不断完善的过程。现在,大型语言模型࿰…...
Beekeeper Studio:现代跨平台数据库管理工具的技术架构与实战应用
Beekeeper Studio:现代跨平台数据库管理工具的技术架构与实战应用 【免费下载链接】beekeeper-studio beekeeper-studio/beekeeper-studio: Beekeeper Studio 是一款开源的跨平台数据库客户端工具,支持多种数据库(如MySQL, PostgreSQL, SQLit…...
水下机器人导航的‘感官进化’:从纯视觉VIO到声光惯压融合的SVIn2系统拆解
水下机器人导航的‘感官进化’:从纯视觉VIO到声光惯压融合的SVIn2系统拆解 当一台水下机器人潜入浑浊的湖泊执行管道巡检任务时,它的视觉传感器突然失效——悬浮颗粒使画面变成乳白色噪点,而水流扰动让惯性测量单元(IMU)数据充满噪声。这正是…...
5分钟制作Windows启动盘:Rufus免费工具终极指南
5分钟制作Windows启动盘:Rufus免费工具终极指南 【免费下载链接】rufus The Reliable USB Formatting Utility 项目地址: https://gitcode.com/GitHub_Trending/ru/rufus 还在为系统重装而烦恼吗?Rufus作为一款完全免费的USB格式化工具࿰…...
如何高效提取网页SVG内容:3步实现可视化数据导出
如何高效提取网页SVG内容:3步实现可视化数据导出 【免费下载链接】svg-crowbar Extracts an SVG node and accompanying styles from an HTML document and allows you to download it all as an SVG file. 项目地址: https://gitcode.com/gh_mirrors/sv/svg-crow…...
比较好的金线包封胶制造商推荐几家
嘿,朋友们!在半导体封装领域,金线包封胶就像是芯片的“贴身保镖”,保护着纤细的金线,让芯片能够稳定工作。今天咱们就来聊聊比较好的金线包封胶制造商,看看哪家更值得你选择。一、东莞市汉思新材料科技有限…...
Ubuntu 24.04镜像源配置全攻略:从原理到实战(含常见报错解决)
Ubuntu 24.04镜像源深度解析与高效配置实战 最近在帮朋友配置新装的Ubuntu 24.04时,发现这个版本在软件源管理上做了重大调整——从传统的sources.list文件变成了结构化更强的sources.d目录配置方式。这个变化让不少习惯了旧版本的用户感到困惑,也让我意…...
中国跨境电商大会代理授权机制与决策影响分析
对于众多寻求通过“中国跨境电商大会”精准撬动海外市场的企业而言,面对琳琅满目的代理商选择,决策过程本身就是一次关于市场洞察、风险评估与资源匹配的深度考验。一个优质的代理商,不仅是展位的“售票员”,更是企业出海战略的“…...
