mybatis数据加解密处理方案

1.背景

  为了防止数据库的用户数据安全,所以需要对用户数据进行加密,具体为插入数据进行加密,查询数据自动解密。

2.方案

  查询相关文档后,发现mybatis有2种方案可以处理:

   a.使用typeHandler

   b.使用intercept

   经过对批量数据执行后,发现千、万、百万级别数据拦截器相对更快一些。

3.具体实现

  3.1 intercept

   a.注解

   EncryptDecryptData 该注解用于标记拦截器适用的DBEntity

@Inherited @Target({ ElementType.TYPE }) @Retention(RetentionPolicy.RUNTIME) public @interface EncryptDecryptData { }
EncryptDecryptData

  EncryptDecryptField 该注解用于标记拦截器用于加密的字段

@Inherited @Target({ ElementType.FIELD }) @Retention(RetentionPolicy.RUNTIME) public @interface EncryptDecryptField { }
EncryptDecryptField

  b.加密方法

import java.lang.reflect.Field; import java.util.Objects; public class EncryptDecrypt { private final static String key = "asffqqas"; /** * 加密 * * @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加密工具 field.set(paramsObject, DesUtil.encrypt(value,key)); } } } 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; //对注解的字段进行逐一解密 field.set(result, DesUtil.decrypt(value,key)); } } } return result; } }
EncryptDecrypt

  c.拦截器

写入数据进行加密(insert)

@Intercepts({ @Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class), }) public class WriteInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //@Signature 指定了 type= parameterHandler 后,这里的 invocation.getTarget() 便是parameterHandler //若指定ResultSetHandler ,这里则能强转为ResultSetHandler ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget(); // 获取参数对像,即 mapper 中 paramsType 的实例 Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject"); parameterField.setAccessible(true); //取出实例 Object parameterObject = parameterField.get(parameterHandler); if (parameterObject != null) { Class<?> parameterObjectClass = parameterObject.getClass(); //校验该实例的类是否被@EncryptDecryptData所注解 EncryptDecryptData encryptDecryptData = AnnotationUtils.findAnnotation(parameterObjectClass, EncryptDecryptData.class); if (Objects.nonNull(encryptDecryptData)) { //取出当前当前类所有字段,传入加密方法 Field[] declaredFields = parameterObjectClass.getDeclaredFields(); EncryptDecrypt.encrypt(declaredFields, parameterObject); } } return invocation.proceed(); } @Override public Object plugin(Object o) { //这里必须写入,会判定是否把当前拦截器启动 return Plugin.wrap(o, this); } }
View Code

 查询数据进行解密

@Intercepts({ @Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class}) }) public class ReadInterceptor implements Interceptor { @Override public Object intercept(Invocation invocation) throws Throwable { //取出查询的结果 Object resultObject = invocation.proceed(); if (Objects.isNull(resultObject)) { return null; } //基于selectList if (resultObject instanceof ArrayList) { ArrayList resultList = (ArrayList) resultObject; if (!CollectionUtils.isEmpty(resultList) && needToDecrypt(resultList.get(0))) { for (Object result : resultList) { //逐一解密 EncryptDecrypt.decrypt(result); } } //基于selectOne } else { if (needToDecrypt(resultObject)) { EncryptDecrypt.decrypt(resultObject); } } return resultObject; } private boolean needToDecrypt(Object object) { Class<?> objectClass = object.getClass(); EncryptDecryptData sensitiveData = AnnotationUtils.findAnnotation(objectClass, EncryptDecryptData.class); return Objects.nonNull(sensitiveData); } @Override public Object plugin(Object target) { return Plugin.wrap(target, this); } }
View Code
@Intercepts mybatis的注解,用于标记这是一个拦截器,@Signature则表明要拦截的接口、方法以及对应的参数类型,主要类型有如下:
type method 备注
Executor update, query, flushStatements, commit, rollback, getTransaction, close, isClosed 拦截执行器的方法
ParameterHandler getParameterObject, setParameters 拦截参数的处理
ResultSetHandler handleResultSets, handleOutputParameters 拦截结果集的处理
StatementHandler prepare, parameterize, batch, update, query 拦截Sql语法构建的处理

 d.使用注解

@Data @TableName("user") @EncryptDecryptData public class UserDBEntity implements Serializable { @TableId(value = "user_id",type = IdType.AUTO) private Integer userId; @EncryptDecryptField @TableField("address") private String address; @EncryptDecryptField @TableField("mobile") private String mobile; }
View Code

 e.注册拦截

  在生成的sqlSessionFactory中加入拦截器

@Bean(name = "userSqlSessionFactory") public SqlSessionFactory userSqlSessionFactory(@Qualifier("userDB") DataSource dataSource) throws Exception { MybatisSqlSessionFactoryBean bean = new MybatisSqlSessionFactoryBean(); bean.setDataSource(dataSource); //添加插件 bean.setPlugins(new Interceptor[]{new WriteInterceptor(),new ReadInterceptor()}); //省略代码 return bean.getObject(); }
View Code

 f.使用

 使用拦截器时,因为注解是写在UserDBEntity上,所以插入或查询数据时,要传入UserDBEntity对象,例如:

1 @Repository 2 public interface UserMapper { 3 4 @Select("select * from user where mobile = #{mobile} ") 5 UserDBEntity selectByMobile(TestAddressDBEntity mobile); 6 7 8 @Insert(" insert into user (user_id,mobile,address) values (#{userId},#{mobile},#{address})") 9 int insert(UserDBEntity userInfo); 10 11 12 } 13 14 /*UserDBEntity query = new UserDBEntity(); 15 UserDBEntity.setMobile(13711111111); 16 UserDBEntity s = UserMapper.selectByMobile(query);*/
View Code

 


__EOF__

本文作者Jun10ng
本文链接https://www.cnblogs.com/wangzun/p/15774811.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Johnson_wang  阅读(1578)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示