如果需要对一些特殊数据进行加密、解密处理,查阅部分资料文档,一般会避开在复杂的业务层处理,大部分会在数据访问层进行数据的加密、解密处理。目前比较普遍的两种方式如下:
一、基于自定义类型转换器
主要是使用mybatis框架提供的TypeHandler来实现在持久层处理数据。
- 实现方式
- 持久化实体对象类中对应字段@TableField注解上新增typeHandler属性配置即可。
- Mapper映射文件resultMap标签下对应字段result标签新增typeHandler属性配置即可。
- 注意事项
- 目前验证支持mybatis框架自带的语句可以生效,部分情况也失效,如查询条件包含需要加密解密的字段。
- 不支持自定义语句。
@Slf4j
@MappedTypes({String.class})
@MappedJdbcTypes(JdbcType.VARCHAR)
public class AesEncryptDecryptTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, AesUtils.encryptToHex(parameter, Constants.AES_SECRET));
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return decrypt(rs.getString(columnName));
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return decrypt(rs.getString(columnIndex));
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return decrypt(cs.getString(columnIndex));
}
private String decrypt(String value) {
if (null == value || value.length() == 0) {
return null ;
}
try {
value = AesUtils.decryptFromHex(value, Constants.AES_SECRET);
} catch (Exception e) {
log.error(e.getMessage());
}
return value;
}
}
二、基于拦截器
主要是使用mybatis框架提供的Interceptor来实现在相关操作过程的数据处理。
- 实现方式
- 扩展Interceptor功能,拦截相关操作进行筛选判断实现加密解密。
- 注意事项
- 存在多次判断、反射等操作可能存在效率问题。
- 多种情况还需要单独处理,如查询加密解密字段等。
@Slf4j
@Intercepts({@Signature(type = ParameterHandler.class, method = "setParameters", args = PreparedStatement.class)})
public class ParameterEncryptInterceptor extends ParameterInterceptor implements Interceptor, InitializingBean {
private EncryptDecryptProperties encryptDecryptProperties;
private RsaSolver rsaSolver;
public ParameterEncryptInterceptor(EncryptDecryptProperties encryptDecryptProperties) {
this.encryptDecryptProperties = encryptDecryptProperties;
}
@Override
public void afterPropertiesSet() throws Exception {
EncryptDecryptProperties.AlgorithmConfig algorithmConfig = encryptDecryptProperties.getAlgorithmConfig();
if (Constants.ALGORITHM_RSA.equalsIgnoreCase(algorithmConfig.getName())) {
String rsaBeanName = algorithmConfig.getRsaBeanName();
if (CharSequenceUtil.isNotBlank(rsaBeanName) && applicationContext.containsBean(rsaBeanName)) {
rsaSolver = applicationContext.getBean(rsaBeanName, RsaSolver.class);
}
}
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
return handleInvocation(invocation);
}
@Override
public Map<String, List<String>> getObjectFieldsMap() {
return encryptDecryptProperties.getObjectFieldsMap();
}
@Override
public Object handleObjectField(Object objectFieldValue) {
String objectFieldStringValue = String.valueOf(objectFieldValue);
EncryptDecryptProperties.AlgorithmConfig algorithmConfig = encryptDecryptProperties.getAlgorithmConfig();
String algorithmName = algorithmConfig.getName().toUpperCase();
String algorithmSecret = algorithmConfig.getSecret();
switch (algorithmName) {
case Constants.ALGORITHM_DES:
objectFieldStringValue = DesUtils.encryptBy3DesToHex(objectFieldStringValue, algorithmSecret);
break;
case Constants.ALGORITHM_AES:
objectFieldStringValue = AesUtils.encryptToHex(objectFieldStringValue, algorithmSecret);
break;
case Constants.ALGORITHM_RSA:
objectFieldStringValue = rsaSolver.encrypt(objectFieldStringValue.getBytes(StandardCharsets.UTF_8));
break;
default:
break;
}
return objectFieldStringValue;
}
}
@Slf4j
public abstract class ParameterInterceptor extends AbstractInterceptor {
protected Object handleInvocation(Invocation invocation) throws Throwable {
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
MetaObject metaObject = MetaObject.forObject(parameterHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
Object parameterObject = parameterHandler.getParameterObject();
if (null == parameterObject) {
return invocation.proceed();
}
Class<Object> parameterizedType = getParameterizedType((MappedStatement) metaObject.getValue("mappedStatement"));
List<String> fields = getObjectFieldsMap().get(parameterizedType.getName());
if (null == fields || fields.isEmpty()) {
return invocation.proceed();
}
if (parameterObject instanceof Map) {
Map<String, Object> parameterMapObject = (Map<String, Object>) parameterObject;
if (parameterMapObject.containsKey(com.baomidou.mybatisplus.core.toolkit.Constants.ENTITY)) {
Object etObject = parameterMapObject.get(com.baomidou.mybatisplus.core.toolkit.Constants.ENTITY);
if (null != etObject) {
handleObjectFields(etObject);
}
} if (parameterMapObject.containsKey(com.baomidou.mybatisplus.core.toolkit.Constants.WRAPPER)) {
handleObjectFields(metaObject, fields);
} else {
handleObjectFields(parameterizedType, parameterMapObject);
}
} else {
handleObjectFields(parameterObject);
}
return invocation.proceed();
}
}