Mybatis自动加解密

涉及隐私信息的字段需要加密存储数据库,返回给前端时又需要解密显示正确信息。故采用mybatis自动加解密的方案,该方案基于自定义注解+拦截器进行实现。加密后的信息不支持模糊匹配(可参考业界流行方案,基于业务需求做分词或采用其他方案以支持模糊匹配,本文不涉及)。

网上的解决方案会有以下问题(已在代码中完善解决,不再细述):

1.对于对象List中的需加密属性没有加密

2.重复加密,导致密文有误

1.引入hutool-all依赖(加解密所使用依赖)

<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.10</version>
</dependency>

2.定义加解密工具类

//加解密工具类
public class EncryptDecryptUtil {private final static Logger logger = LoggerFactory.getLogger(EncryptDecryptUtil.class);/*** 对称加密的秘钥*/private final static String key = "加密密钥";/*** * 加密     ** * @param declaredFields paramsObject所声明的字段* * @param paramsObject   mapper中paramsType的实例* * @return T* * @throws IllegalAccessException 字段不可访问异常*/public static <T> T encrypt(Field[] declaredFields, T paramsObject) throws IllegalAccessException {for (Field field : declaredFields) {            //取出所有被EncryptDecryptField注解的字段EncryptDecryptField sensitiveField = field.getAnnotation(EncryptDecryptField.class);if (!Objects.isNull(sensitiveField)) {field.setAccessible(true);Object object = field.get(paramsObject);                //暂时只实现String类型的加密if (object instanceof String) {String value = (String) object;                    //加密  Des加密工具String encryptHexStr = SecureUtil.des(key.getBytes()).encryptHex(value);//logger.info(value+"加密后是"+encryptHexStr);field.set(paramsObject, encryptHexStr);}}}return paramsObject;}/*** 解密* * @param result resultType的实例* * @return T* * @throws IllegalAccessException 字段不可访问异常*/public static <T> T decrypt(T result) throws IllegalAccessException {//取出resultType的类Class<?> resultClass = result.getClass();Field[] declaredFields = resultClass.getDeclaredFields();for (Field field : declaredFields) {//取出所有被EncryptDecryptField注解的字段EncryptDecryptField sensitiveField = field.getAnnotation(EncryptDecryptField.class);if (!Objects.isNull(sensitiveField)) {field.setAccessible(true);Object object = field.get(result);                //只支持String的解密if (object instanceof String) {String value = (String) object;//对注解的字段进行逐一解密String decryptStr = SecureUtil.des(key.getBytes()).decryptStr(value);//logger.info("{}字段需要解密,{}解密后的值是{}",field.getName(),value,decryptStr);field.set(result, decryptStr);}}}return result;}}

3.创建自定义注解类

/*** 该注解定义在类上* 插件通过扫描类对象是否包含这个注解来决定是否继续扫描其中的字段注解* 这个注解要配合EncryptDecryptField注解**/
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptClass {
}
/*** 该注解有两种使用方式* ①:配合@EncryptDecryptClass加在类中的字段上* ②:直接在Mapper中的方法参数上使用**/
@Inherited
@Target({ ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptField {
}

4.配置自定义的mybatis读写拦截器

//写入数据拦截器
@Intercepts({@Signature(type = ParameterHandler.class,method = "setParameters",args = PreparedStatement.class)
})
public class WriteInterceptor implements Interceptor {private final Logger logger = LoggerFactory.getLogger(this.getClass());@Overridepublic Object intercept(Invocation invocation) throws Throwable {ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();Object testParame=parameterHandler.getParameterObject();Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");parameterField.setAccessible(true);        //取出实例Object parameterObject = parameterField.get(parameterHandler);if (parameterObject != null) {Map<String,Object> test= BeanUtil.beanToMap(parameterObject);Collection values = test.values();Set<Object> setObj = new LinkedHashSet<>(values);for (Object value : setObj) {if(value instanceof List){for(Object item : (List<?>) value){Class<?> parameterObjectClass = item.getClass();//校验该实例的类是否被EncryptDecryptClass所注解EncryptDecryptClass encryptDecryptClass =AnnotationUtils.findAnnotation(parameterObjectClass, EncryptDecryptClass.class);if (Objects.nonNull(encryptDecryptClass)) {//取出当前当前类所有字段,传入加密方法Field[] declaredFields = parameterObjectClass.getDeclaredFields();EncryptDecryptUtil.encrypt(declaredFields, item);}}}else {Class<?> parameterObjectClass = value.getClass();//校验该实例的类是否被EncryptDecryptClass所注解EncryptDecryptClass encryptDecryptClass =AnnotationUtils.findAnnotation(parameterObjectClass, EncryptDecryptClass.class);if (Objects.nonNull(encryptDecryptClass)) {//取出当前当前类所有字段,传入加密方法Field[] declaredFields = parameterObjectClass.getDeclaredFields();EncryptDecryptUtil.encrypt(declaredFields, value);}}}}return invocation.proceed();}@Overridepublic Object plugin(Object o) {//这里必须写入,会判定是否把当前拦截器启动return Plugin.wrap(o, this);}
}
//读取数据拦截器
@Intercepts({@Signature(type = ResultSetHandler.class,method = "handleResultSets",args = {Statement.class})})
public class ReadInterceptor implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {//取出查询的结果Object resultObject = invocation.proceed();if (Objects.isNull(resultObject)) {return null;}//基于selectListif (resultObject instanceof ArrayList) {ArrayList resultList = (ArrayList) resultObject;if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) {for (Object result : resultList) {//逐一解密EncryptDecryptUtil.decrypt(result);}}//基于selectOne} else {if (needToDecrypt(resultObject)) {EncryptDecryptUtil.decrypt(resultObject);}}return resultObject;}private boolean needToDecrypt(Object object) {Class<?> objectClass = object.getClass();EncryptDecryptClass sensitiveData =AnnotationUtils.findAnnotation(objectClass, EncryptDecryptClass.class);return Objects.nonNull(sensitiveData);}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}
}
//配置自定义的mybatis拦截器
@Configuration
@Component
public class MybatisCryptoConfig {@Beanpublic ReadInterceptor readInterceptorPlugin(){return new ReadInterceptor();}@Beanpublic WriteInterceptor writeInterceptorPlugin(){return new WriteInterceptor();}
}

5.在实体类上使用注解

在涉及需要加解密的实体类上使用@EncryptDecryptClass注解,在需要加解密的属性上使用@EncryptDecryptField注解。

/*** 管理员信息*/
@Data
@EncryptDecryptClass
public class SysAdminVo {/*** 管理员id* @mock 123455*/private String admin_id;/*** 管理员姓名* @mock 张三*/private String admin_name;/*** 管理员手机号;* @mock 13112341234*/@EncryptDecryptFieldprivate String admin_phone;}

6.使用说明

在需要自动加解密的时候,请使用已配置注解的实体类作为入参或出参,否则不会进行加解密转换。

参考链接:

【Mybatis】基于Mybatis插件+注解,实现敏感数据自动加解密

原生mybatis实现数据加密存储和读取

springboot+mybatis+自定义注解实现对数据库中的字段进行加解密

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/615477.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

与听力学相关的职业都有哪些?怎么选择?

随着年龄的增长&#xff0c;每个人都可能面临听觉障碍的困惑。听力学领域专注于患者的耳朵问题&#xff0c;包括听力损失和平衡障碍。听力学职业是为患者提供听觉健康管理服务的职业&#xff0c;专注于他们耳朵的听力和平衡甚至言语相关需求。为患者进行听功能检查、测试、诊疗…

1.5如何用命令得到自己的ip<本地>

专栏导航 第四章 具有通用性的花生壳ddns脚本 第五章 如何用命令得到自己的ip<本地> ⇐ 第六章 用命令得到ip和域名解析<网络>() 用折腾路由的兴趣,顺便入门shell编程。 第五章 如何用命令得到ip<本地> 文章目录 专栏导航第五章 如何用命令得到ip<本地…

代码随想录算法训练营Day17|110.平衡二叉树、257. 二叉树的所有路径、 404.左叶子之和

文章目录 一、110.平衡二叉树1.递归法 二、257. 二叉树的所有路径1. 递归法 三、 404.左叶子之和1.迭代法 一、110.平衡二叉树 题目描述&#xff1a; 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a;一个二…

Python操作MongoDB快速入门教程

Docker安装MongoDB 拉取镜像&#xff1a; docker pull mongo:6.0.2创建容器&#xff1a; docker run --name mongo -d -p 27017:27017 mongo:6.0.2设置用户名和密码&#xff1a; # 创建mongo容器后&#xff0c;进入容器 docker exec -it mongo bash# 进入mongo shell mongo…

C++ Primer 6.2参数传递 知识点+练习题

C Primer 6.2参数传递 知识点练习题 指针形参使用引用拷贝Const 形参实参尽量使用常量引用数组形参数组引用形参传递多维数组向main函数传参数含有可变形参的函数练习题待更新 指针形参 void reset(int *p) {*p0;//p指向的整型对象变为0p0;//只是对形参改变p&#xff0c;使其为…

【Git】查看凭据管理器的账号信息,并删除账号,解决首次认证登录失败后无法重新登录的问题

欢迎来到《小5讲堂》 大家好&#xff0c;我是全栈小5。 这是是《代码管理工具》序列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对知识点的…

Axios基本使用,为学习后续的Vue服务【发送请求+并发请求+前端拦截器】

目录 1、项目中引入Axios 2、使用Axios发送请求 2.1、例&#xff1a;发送GET请求 2.2、例&#xff1a;发送POST请求 3、axios并发请求 4、拦截器 1、项目中引入Axios <script src"https://unpkg.com/axios/dist/axios.min.js"></script> 注&…

性能测试分析案例-定位内核线程CPU利用率太高

环境准备 预先安装 docker、perf、hping3、curl 等工具&#xff0c;如 apt install docker.io linux-tools-common hping3 操作和分析 Linux 在启动过程中&#xff0c;有三个特殊的进程&#xff0c;也就是 PID 号最小的三个进程。 0 号进程为 idle 进程&#xff0c;这也是系…

力扣_数组28—子集

题目 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。 解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。 输入&#xff1a; n u m s [ 1 , 2 , 3 ] nums [1,2,3] nums[1,2,3] 输出&#xff1a…

Windows 项目从0到1的部署

目录 一. 安装jdk 1.1 安装jdk 1.2 配置jdk的环境配置jdk 1.3 配置成功 二. 配置tomcat 2.1 启动tomcat 2.2 防火墙设置 三. 安装MySQL 3.1 安装步骤 3.2 内部连接 3.3 外部连接 四. 部署项目 4.1 项目部署 4.2 修改mysql的用户密码 一. 安装jdk 这里给大家准备好了jdk和…

2024.1.8 Day04_SparkCore_homeWork

目录 1. 简述Spark持久化中缓存和checkpoint检查点的区别 2 . 如何使用缓存和检查点? 3 . 代码题 浏览器Nginx案例 先进行数据清洗,做后续需求用 1、需求一&#xff1a;点击最多的前10个网站域名 2、需求二&#xff1a;用户最喜欢点击的页面排序TOP10 3、需求三&#x…

【Kubernetes】K8s 查看 Pod 的状态

K8s 查看 Pod 的状态 [rootk8s-master1 ~]# kubectl get pods NAME READY STATUS RESTARTS AGE nginx-3 1/1 Running 2 (34m ago) 14hNAME&#xff1a;Pod 的名称。READY&#xff1a;代表 Pod 里面有几个容器&#xff0c;前面是启动的&#xff0c;后面…

一卡通水控电控开发踩过的坑

最近在做一个项目&#xff0c;是对接一卡通设备的。我一开始只拿到设备和3个文档开局。不知道从哪下手。一步一步踩坑过来。踩了很多没有必要的坑&#xff0c;写出来给有用的人吧。 读卡器怎么用&#xff1f; 有个读卡器&#xff0c;一开始什么软件也不提供。我都不知道是干嘛…

Jupyter Notebook

2017年左右在大学里都听说过Jupyter Notebook&#xff0c;并且也安装用了一段时间&#xff0c;后来不知道什么原因没有用了。估计是那时候写代码的时候多一些&#xff0c;因为它可以直接写代码并运行结果&#xff0c;现在不怎么写代码了。 介绍 后缀名为.ipynb的json格式文件…

laravel框架的用途有哪些

Laravel框架是一个开源的PHP框架&#xff0c;用于开发Web应用程序。它有很多用途&#xff0c;包括&#xff1a; Web应用程序开发&#xff1a;Laravel框架提供了丰富的功能和工具&#xff0c;用于开发各种类型的Web应用程序&#xff0c;包括企业应用、电子商务平台、博客、社交…

Kotlin-数组

数组 创建数组 可以通过Array来创建数组&#xff0c;也可以通过arrayOf()等工具创建数组 Kotlin中创建数组有两种方式 arrayOf(),arrayOfNulls()&#xff0c;emptyArray(&#xff09;工具函数使用Array&#xff08;size:int&#xff0c;initial:&#xff08;Int&#xff09;…

《YOLO算法:基础+进阶+改进》报错解决 专栏答疑

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。《YOLO算法&#xff1a;基础进阶改进》专栏上线后&#xff0c;部分同学在学习过程中提出了一些问题&#xff0c;笔者相信这些问题其他同学也有可能遇到。为了让大家可以更好地学习本专栏内容&#xff0c;笔者特意推出了该篇…

SpringBoot集成Camunda

一&#xff1a;SpringBoot集成 1.1&#xff1a;pom.xml 因camunda集成SpringBoot对SpringBoot的版本和JDK的版本都有一定的要求&#xff0c;所以这里贴个完整的依赖。可以去官网找每个SpringBoot的版本对应的camunda版本。 <?xml version"1.0" encoding"…

前端入门教程:学完即可单独完成前端项目

目录 目录 1.HTML: 1.1概念 1.2结构 1.3常见的标签使用分类&#xff1a; 2.CSS: 2.1概念 2.2样式实践&#xff1a; 以下的举例都来自于博客&#xff1a; 2.3css选择器&#xff1a; 什么是css选择器&#xff1a; 举例如下&#xff1a; 2.4Demo 3.JavaScript&#…

Spring 容器的初始化过程

Spring 容器的初始化过程包括以下主要步骤&#xff1a; 加载配置文件&#xff1a; Spring 容器通常会从 XML 文件、Java 注解或者 Java Config 等方式加载配置信息&#xff0c;这些配置信息定义了应用中的 bean、依赖关系、切面等内容。 创建容器实例&#xff1a; 一旦配置…