基于MyBatis-Plus数据库存取字段加解密解法两种

基于MyBatis-Plus数据库存取字段加解密解法两种,第1张

基于MyBatis-Plus数据库存取字段加解密解法两种

基于框架中使用的MyBatis-Plus整理了两个方法,一种是MyBatis-Plus自带的TypeHandler,另一种是基于MyBatis的Intercept拦截器。方法一配置简单使用麻烦,方法二配置麻烦使用简单,自己斟酌使用即可。当然方法二适用只使用MyBatis + Springboot的架构。
MyBatis-Plus版本:3.4.0

一、基于MyBatis-Plus自定义类型处理器(TypeHandler)的方法 1、创建TypeHandler
//AES 是工具方法类,按加密需求设置
public class AESEncryptHandler extends baseTypeHandler {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, AES.encrypt((String)parameter));
    }
    @Override
    public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
        String columnValue = rs.getString(columnName);
        return AES.decrypt(columnValue);
    }
    @Override
    public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        String columnValue = rs.getString(columnIndex);
        return AES.decrypt(columnValue);
    }
    @Override
    public String getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        String columnValue = cs.getString(columnIndex);
        return AES.decrypt(columnValue);
    }
}
2、开启MyBatis-Plus的扫描

在配置文件中加入配置

mybatis-plus.type-handlers-package=com.demo.mybatisplusintercept.intercept

配置说明(来源官方文档):

PS:实测不加这个配置也可以使用,未深入,原因不明

3、实体类中加入注解

类上加入注解

@TableName(autoResultMap = true)

字段上加入注解

@TableField(typeHandler = AESEncryptHandler.class)

4、查询中指定TypeHandler

使用MyBatis-Plus默认方法查询时无需指定,但是当需要使用sql查询的时候,需要指定TypeHandler
注解

XML

二、基于MyBatis的方法(拦截器)

参考文章:MyBatis 插件之拦截器(Interceptor)

在官方示例(https://mybatis.org/mybatis-3/zh/configuration.html#plugins)中,是直接监听Executor,但是通过文章介绍可以看到,ParameterHandler和ResultSetHandler分别 *** 作入参和查询,所以在此处 *** 作字段的加解密应该更为合理。

1、自定义注解

类注解

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

}

字段注解

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

}
2、工具类
package com.demo.mybatisplusintercept.intercept;

import com.demo.mybatisplusintercept.annotation.EncryptDecryptField;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Objects;


public class EncryptDecryptUtils {

	
	public static  T encrypt(Field[] declaredFields, T parameterObject) throws IllegalAccessException {
		for (Field field : declaredFields) {
			EncryptDecryptField annotation = field.getAnnotation(EncryptDecryptField.class);
			if (Objects.isNull(annotation)) {
				continue;
			}
			encrypt(field, parameterObject);
		}
		return parameterObject;
	}


	
	public static  T encrypt(Field field, T parameterObject) throws IllegalAccessException {
		field.setAccessible(true);
		Object object = field.get(parameterObject);
		if (object instanceof BigDecimal) {
			BigDecimal value = (BigDecimal) object;
			long longValue = value.movePointRight(4).subtract(BigDecimal.valueOf(Integer.MAX_VALUE >> 3)).longValue();
			field.set(parameterObject, BigDecimal.valueOf(longValue));
		} else if (object instanceof Integer) {

		} else if (object instanceof Long) {

		} else if (object instanceof String) {
			//定制String类型的加密算法
			String value = (String) object;
			field.set(parameterObject, AES.encrypt(value));
		}
		return parameterObject;
	}

	
	public static  T decrypt(T result) throws IllegalAccessException {
		Class parameterObjectClass = result.getClass();
		Field[] declaredFields = parameterObjectClass.getDeclaredFields();
		decrypt(declaredFields, result);
		return result;
	}

	
	public static void decrypt(Field[] declaredFields, Object result) throws IllegalAccessException {
		for (Field field : declaredFields) {
			EncryptDecryptField annotation = field.getAnnotation(EncryptDecryptField.class);
			if (Objects.isNull(annotation)) {
				continue;
			}
			decrypt(field, result);
		}
	}

	
	public static void decrypt(Field field, Object result) throws IllegalAccessException {
		field.setAccessible(true);
		Object object = field.get(result);
		if (object instanceof BigDecimal) {
			BigDecimal value = (BigDecimal) object;
			double doublevalue = value.add(BigDecimal.valueOf(Integer.MAX_VALUE >> 3)).movePointLeft(4).doublevalue();
			field.set(result, BigDecimal.valueOf(doublevalue));
		} else if (object instanceof Integer) {

		} else if (object instanceof Long) {

		} else if (object instanceof String) {
			//定制String类型的解密算法
			String value = (String) object;
			field.set(result, AES.decrypt(value));

		}
	}


}
3、两个拦截器
@Intercepts({
        @Signature(type = ParameterHandler.class,method = "setParameters",args = PreparedStatement.class)
})
@ConditionalOnProperty(value = "domain.encrypt",havingValue = "true")
@Slf4j
public class MybatisParameterIntercept implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        //拦截 ParameterHandler 的 setParameters 方法 动态设置参数
        if (invocation.getTarget() instanceof ParameterHandler) {
            ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();

            // 反射获取 参数对象
            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 = EncryptDecryptUtils.encrypt(declaredFields, parameterObject);
                }
            }
        }
        return invocation.proceed();
    }
}
@Intercepts({
        @Signature(type = ResultSetHandler.class,method = "handleResultSets",args = Statement.class)
})
@ConditionalOnProperty(value = "domain.encrypt",havingValue = "true")
@Component
@Slf4j
public class MybatisResultSetIntercept implements Interceptor {

    @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++) {
                    EncryptDecryptUtils.decrypt(resultList.get(i));
                }
            }
        }else {
            if (needToDecrypt(result)){
                EncryptDecryptUtils.decrypt(result);
            }
        }
        return result;
    }

    private boolean needToDecrypt(Object object){
        Class objectClass = object.getClass();
        EncryptDecryptClass encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, EncryptDecryptClass.class);
        if (Objects.nonNull(encryptDecryptClass)){
            return true;
        }
        return false;
    }
}
4、注册拦截器
@Configuration
@MapperScan("com.demo.mybatisplusintercept.dao")
public class MyBatisPlusConfig {

    @Autowired
    private ApplicationContext applicationContext;

    
    @Bean
    public String myInterceptor(SqlSessionFactory sqlSessionFactory){

        sqlSessionFactory.getConfiguration().addInterceptor(parameterIntercept());

        sqlSessionFactory.getConfiguration().addInterceptor(resultSetIntercept());

        return "myInterceptor";
    }

    public MybatisParameterIntercept parameterIntercept(){
        return applicationContext.getAutowireCapableBeanFactory().createBean(MybatisParameterIntercept.class);
    }

    public MybatisResultSetIntercept resultSetIntercept(){
        return applicationContext.getAutowireCapableBeanFactory().createBean(MybatisResultSetIntercept.class);
    }

}
5、实体类添加注解
@Data
@TableName(value = "user",autoResultMap = true)
@EncryptDecryptClass
public class User implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(type = IdType.AUTO)
    private Long id;

    private String name;

//    @TableField(typeHandler = AESEncryptHandler.class)
    @EncryptDecryptField
    private String mobile;

//    @TableField(typeHandler = AESEncryptHandler.class)
    @EncryptDecryptField
    private String idcard;

}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5687733.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存