基于框架中使用的MyBatis-Plus整理了两个方法,一种是MyBatis-Plus自带的TypeHandler,另一种是基于MyBatis的Intercept拦截器。方法一配置简单使用麻烦,方法二配置麻烦使用简单,自己斟酌使用即可。当然方法二适用只使用MyBatis + Springboot的架构。
MyBatis-Plus版本:3.4.0
//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);
}
}
在配置文件中加入配置
mybatis-plus.type-handlers-package=com.demo.mybatisplusintercept.intercept
PS:实测不加这个配置也可以使用,未深入,原因不明
类上加入注解
@TableName(autoResultMap = true)
字段上加入注解
@TableField(typeHandler = AESEncryptHandler.class)
使用MyBatis-Plus默认方法查询时无需指定,但是当需要使用sql查询的时候,需要指定TypeHandler
注解
XML
参考文章:MyBatis 插件之拦截器(Interceptor)
在官方示例(https://mybatis.org/mybatis-3/zh/configuration.html#plugins)中,是直接监听Executor,但是通过文章介绍可以看到,ParameterHandler和ResultSetHandler分别操作入参和查询,所以在此处操作字段的加解密应该更为合理。
类注解
@Documented
@Inherited
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptClass{
}
字段注解
@Documented
@Inherited
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface EncryptDecryptField {
}
package com.demo.mybatisplusintercept.intercept;
import com.demo.mybatisplusintercept.annotation.EncryptDecryptField;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.Objects;
/**
* Domain数据加密工具类
*
*/
public class EncryptDecryptUtils {
/**
* 多field加密方法
*
* @param declaredFields
* @param parameterObject
* @param
* @return
* @throws IllegalAccessException
*/
public static <T> 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;
}
/**
* 单个field加密方法
*
* @param field
* @param parameterObject
* @param
* @return
* @throws IllegalAccessException
*/
public static <T> 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;
}
/**
* 解密方法
*
* @param result
* @param
* @return
* @throws IllegalAccessException
*/
public static <T> T decrypt(T result) throws IllegalAccessException {
Class<?> parameterObjectClass = result.getClass();
Field[] declaredFields = parameterObjectClass.getDeclaredFields();
decrypt(declaredFields, result);
return result;
}
/**
* 多个field解密方法
*
* @param declaredFields
* @param result
* @throws IllegalAccessException
*/
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);
}
}
/**
* 单个field解密方法
*
* @param field
* @param result
* @throws IllegalAccessException
*/
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));
}
}
}
@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;
}
}
/**
* 注册mybatis拦截器
*/
@Configuration
@MapperScan("com.demo.mybatisplusintercept.dao")
public class MyBatisPlusConfig {
@Autowired
private ApplicationContext applicationContext;
/**
* 注册MyBatis拦截器
* @param sqlSessionFactory
* @return
*/
@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);
}
}
@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;
}