如何使用Mybatis的拦截器实现数据加密与解密
点击蓝色“程序猿DD”关注我哟
加个“星标”,不忘签到哦
转载自公众号:日拱一兵
关注我,回复口令获取可获取独家整理的学习资料:
- 001 :领取《Spring Boot基础教程》
- 002 :领取《Spring Cloud基础教程》
>>当当大促,160买400的书,点击进入<<
拦截器介绍
Mybatis Interceptor
在 Mybatis 中被当作 Plugin(插件),不知道为什么,但确实是在 org.apache.ibatis.plugin
包下面。
既然是拦截器,可以拦截哪些内容呢?试想一下...... 当程序写到持久层时,Mybatis 会 执行 指定 SQL 语句,并处理 请求参数 和 返回值。没错,Mybatis 拦截器可以帮助我们处理上述内容,请看官网的 Plugins 的片段, 内容不多
// 执行
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
// 请求参数处理
ParameterHandler (getParameterObject, setParameters)
// 返回结果集处理
ResultSetHandler (handleResultSets, handleOutputParameters)
// SQL语句构建
StatementHandler (prepare, parameterize, batch, update, query)
拦截器的使用
如果需要实现自定义的拦截器,只需要实现 org.apache.ibatis.plugin.Interceptor
接口,该接口有三个方法:
Object intercept(Invocation invocation) throws Throwable; Object plugin(Object target); void setProperties(Properties properties);
我们要实现数据加密,进入数据库的字段不能是真实的数据,但是返回来的数据要真实可用,所以我们需要针对 Parameter 和 ResultSet 两种类型处理,同时为了更灵活的使用,我们需要自定义注解
自定义注解
类注解,将注解放在实体类上
/** * 需要加解密的类注解 */
@Documented
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptClass {
}
字段注解,将注解放在实体字段上
/** * 加密字段注解 */
@Documented
@Inherited
@Target({ ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptField { }
有了这两个注解,我们可以在我们可以标记我们要处理的实体和实体中的字段
自定义参数处理拦截器
参考官网,通过 @Intercepts
和 @Signature
的联合使用,指定 ParameterHandler.class
类型,同时通过 @Component
注解注入到容器中,即可在设置参数的时候进行拦截,通过自定义接口 IEncryptDecrypt
, 根据 Field 的各种类型自定义加密解密算法
@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class),
})
@ConditionalOnProperty(value = "domain.encrypt", havingValue = "true")
@Component
@Slf4j
public class ParammeterInterceptor implements Interceptor { @Autowired private IEncryptDecrypt encryptDecrypt; @Override public Object intercept(Invocation invocation) throws Throwable { log.info("拦截器ParamInterceptor"); //拦截 ParameterHandler 的 setParameters 方法 动态设置参数 if (invocation.getTarget() instanceof ParameterHandler) { ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget(); PreparedStatement ps = (PreparedStatement) invocation.getArgs()[0]; // 反射获取 BoundSql 对象,此对象包含生成的sql和sql的参数map映射 /*Field boundSqlField = parameterHandler.getClass().getDeclaredField("boundSql"); boundSqlField.setAccessible(true); BoundSql boundSql = (BoundSql) boundSqlField.get(parameterHandler);*/ // 反射获取 参数对像 Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject"); parameterField.setAccessible(true); Object parameterObject = parameterField.get(parameterHandler); if (Objects.nonNull(parameterObject)){ Class<?> parameterObjectClass = parameterObject.getClass(); EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(parameterObjectClass, EncryptDecryptClass.class); if (Objects.nonNull(encryptDecryptClass)){ Field[] declaredFields = parameterObjectClass.getDeclaredFields(); final Object encrypt = encryptDecrypt.encrypt(declaredFields, parameterObject); } } } return invocation.proceed(); } @Override public Object plugin(Object o) { return Plugin.wrap(o, this); } @Override public void setProperties(Properties properties) { }
}
同样新建结果集拦截器
结果集拦截器
与参数拦截器基本一样, 只不过类型指定为 ResultSetHandler.class
@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args={Statement.class})
})
@ConditionalOnProperty(value = "domain.decrypt", havingValue = "true")
@Component
@Slf4j
public class ResultInterceptor implements Interceptor { @Autowired private IEncryptDecrypt encryptDecrypt; @Override public Object intercept(Invocation invocation) throws Throwable { Object result = invocation.proceed(); if (Objects.isNull(result)){ return null; } if (result instanceof ArrayList) { ArrayList resultList = (ArrayList) result; if (CollectionUtils.isNotEmpty(resultList) && needToDecrypt(resultList.get(0))){ for (int i = 0; i < resultList.size(); i++) { encryptDecrypt.decrypt(resultList.get(i)); } } }else { if (needToDecrypt(result)){ encryptDecrypt.decrypt(result); } } return result; } public boolean needToDecrypt(Object object){ Class<?> objectClass = object.getClass(); EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, EncryptDecryptClass.class); if (Objects.nonNull(encryptDecryptClass)){ return true; } return false; } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } @Override public void setProperties(Properties properties) { }
}
加密解密接口
IEncryptDecrypt
接口定义了 加密和解密两个方法:
public interface IEncryptDecrypt { /** * 加密方法 * @param declaredFields 反射bean成员变量 * @param parameterObject Mybatis入参 * @param <T> * @return */ public <T> T encrypt(Field[] declaredFields, T parameterObject) throws IllegalAccessException; /** * 解密方法 * @param result Mybatis 返回值,需要判断是否是ArrayList类型 * @param <T> * @return */ public <T> T decrypt(T result) throws IllegalAccessException;
}
两个拦截器通过在 YAML 中配置属性,按条件注入,外加自定义加密解密算法,完成全局灵活的配置。
核心代码已上传至 Github Demo
问题彩蛋
也许应对当前的业务,看了该文章满足了当下需求,我们目前只看到了什么是 Mybatis 拦截器,怎样简单使用,拦截器的其他用法以及其他很多为什么都没有解决,关注公众号,回复“人迹罕至” 读完文章 「程序猿为什么要看源码」后 ,我不会满足眼前的这些基本应用,我会有诸多疑问,
我们日常写 CRUD 的业务,为什么 Executor 中只有 R(query) 和 U(update), 那么C(insert) 和 D(delete) 怎样处理的?
自定义拦截器是以什么方式被执行的,执行顺序是什么?
分页也是 Mybatis 拦截器的一种,带有分页的框架是怎样使用拦截器的呢?如
Mybatis Plus
,PageHelper
虽然重写了 Inteceptor 接口的
public void setProperties(Properties properties)
方法,但是并没有写什么业务逻辑,这个方法能怎样使用?......
后续文章也会通过读源码的方式逐步解析这些问题,当然你有相关问题也可以留言交流讨论
提高效率工具
依旧推荐在写文章时用到的高效工具,后续相关工具也会在文章中陆续更新,请持续关注
MyBatis Log Plugin
MyBatis Log Plugin 是 Intelligj IDEA 的一个插件,用来从 Mybatis 输出的 log 中提取出当前调用的 SQL 语句,并将参数封装在 SQL 语句中组成完整的 SQL,这样,当我们调试的时候更加清晰方便,可以轻松定位是否 SQL 又问题
推荐阅读:
Maven 虐我千百遍,我待 Maven 如初恋
初探性能优化:2个月到4小时的性能提升
MySQL跑在CentOS 6 和 7上的性能比较
Spring Boot 配置文件中的花样,看这一篇足矣!
Mybatis的工作原理,你了解过吗?
活动介绍:自律到极致-人生才精致:第8期
活动奖励:《小灰的算法之旅》* 10
扫描下面二维码签到参与
关注我,加个星标,不忘签到哦~
2019
与大家聊聊技术人的斜杠生活
⬇️⬇️点一点小惊喜在等你
如何使用Mybatis的拦截器实现数据加密与解密相关推荐
- Mybatis Interceptor 拦截器原理 源码分析
Mybatis采用责任链模式,通过动态代理组织多个拦截器(插件),通过这些拦截器可以改变Mybatis的默认行为(诸如SQL重写之类的),由于插件会深入到Mybatis的核心,因此在编写自己的插件前最 ...
- mybatis使用拦截器显示sql,使用druid配置连接信息
mybatis使用拦截器显示sql,使用druid配置连接信息 mybatis sql Druid 1.显示出sql内容: 新建2个类: MybatisInterceptor :拦截sql,并获得输出 ...
- Mybatis 通过拦截器动态修改SQL
01 使用场景 当我们在多租户的项目中,编写SQL语句都要带上tenant字段,用于区分不同的租户只能操作自己的数据. 比如,像下面的SQL select * from member where id ...
- mybatis 自定义拦截器
拦截器注解 mybatis自定义拦截器实现步骤: 实现org.apache.ibatis.plugin.Interceptor接口. 添加拦截器注解org.apache.ibatis.plugin.I ...
- 一步步教你mybatis分页,mybatis分页拦截器 使用,mybatis拦截器分页
mybatis 分页详解.mybatis分页查询,mybatis分页拦截器使用.struts2下mybatis分页 mybatis默认是支持分页的,内部通过创建可滚动的Result ...
- 使用拦截器进行数据加解密
文章目录 使用拦截器进行数据加解密 1. 加解密工具 3. 加解密字段注解 3.1 加密注解 3.2 解密注解 4. 封装加解密工具 5. 拦截器 6. 不同框架配置说明 6.1 springboot ...
- Mybatis SQL拦截器实现
欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文原文阅读:https:/ ...
- MyBatis框架 拦截器简单使用
Interceptor 是MyBatis提供的一个插件(plugin扩展).代表拦截器,可以拦截代码中的数据库访问操作,即Statement操作 拦截后,可以去修改正在执行的SQL语句,可以额外访问数 ...
- Mybatis Interceptor 拦截器
拦截器(Interceptor)在 Mybatis 中被当做插件(plugin)对待,官方文档提供了 Executor(拦截执行器的方法),ParameterHandler(拦截参数的处理),Resu ...
最新文章
- zookeeper集群启动报错:Cannot open channel to * at election address /ip:3888
- 【Netty】从 BIO、NIO 聊到 Netty
- Shell 与Python的交互
- 数据中心IT机房末端气流组织管理
- hdu5387(模拟)
- pc控制iphone的软件_如何通过“共享文件夹”实现iPhone与PC间文件快速传输
- hadoop学习3 查找块的位置
- 【基础】嵌入式浏览器移植基本要素
- java里pom.xml是啥意思_pom.xml详解
- rhel 6.4 增加光盘为yum repo
- [C++]实现10以内整数的简单科学计算器
- 人工智能教学解决方案
- 科学的软件测试培训时间是多久?
- 运放实现方波三角波发生器-总结报告
- 数据防泄密·工控安全保障方案
- 数字变成大写的类,把人民币转化为大写汉字
- 最新 android系统 设备 分布情况,CNCERT 2018年第一季度国内操作系统及浏览器占比情况分析...
- Java基础知识入门级!
- 2021校园PHP表白墙程序源码
- 投影串口测试程序_关于串口控制投影机的操作方法的几个步骤
热门文章
- linux shell 小数计算
- linux flatpak 简介 同一个应用在不同linux发行版运行
- /lib64/libc.so.6: version `GLIBC_2.14' not found问题
- Cannot send session cache limiter - headers already sent错误解决方法
- 在Windows 8下成功安装.Net3.5的方法
- ubuntu c/c++ IDE编程环境
- Design Pattern - Observer(C#)
- mysql查询时间between and_Mysql中用between...and...查询日期时注意事项
- 删除svn同步文件目录
- 专题 11 IPC之管道