背景需求
对表中的某些字段进行加密,查询之后对字段进行解密,业务代码无需关注加解密环节。
mybatis 拦截器 vs 类型转换器
mybatis的拦截器能实现上面的需求,但是会拦截所有的sql语句,如果项目中只是部分sql涉及到加解密操作,还是比较牺牲大局的。实现起来也比较麻烦,特别是单参数查询的时候,比如参数名称name,此时需要对name进行加密就相对困难。如果是多参数,因为底层会将参数封装成Map,可以根据是否含有指定的key进行加密。
mybatis类型转换器也可以实现上面的需求,方案为修改xml里面的配置,指定类型转换器,并在转换器里面实现数据的加解密。
mybatis 类型转换器
java有java的数据类型,数据库有数据库的数据类型,那么我们在往数据库中插入数据的时候是如何把java类型当做数据库类型插入数据库,在从数据库读取数据的时候又是如何把数据库类型当做java类型来处理呢?这中间必然要经过一个类型转换。
mybatis提供了TypeHandler接口,抽象类BaseTypeHandler实现TypeHandler接口,String,Integer,Long等常见的数据类型转换都是基于继承BaseTypeHandler实现,BaseTypeHandler提供了基本的判空等操作。因此我们可以继承BaseTypeHandler或者实现TypeHandler接口完成自定义类型转换,一般建议继承BaseTypeHandler来编码。

数据加解密类型转换
假设加解密采用AES算法,此处对加解密结果做一下缓存,因为一些数据是一样的,如果每次都去加密,也是会消耗一定的时间
public class DataSecurityHandler extends BaseTypeHandler {
public static final LoadingCache<String, String> ENCRYPT_CACHE = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(new CacheLoader<String, String>() {
public String load(String parameterValue) {
return AES.encrypt(parameterValue);
}
});
public static final LoadingCache<String, String> DECRYPT_CACHE = CacheBuilder.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES).build(new CacheLoader<String, String>() {
public String load(String parameterValue) {
return AES.decrypt(parameterValue);
}
});
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, Object parameter, JdbcType jdbcType) throws SQLException {
String parameterValue = (String) parameter;
String resultValue = ENCRYPT_CACHE.getUnchecked(StringUtils.trimToEmpty(parameterValue));
preparedStatement.setString(i, resultValue);
}
@Override
public Object getNullableResult(ResultSet resultSet, String s) throws SQLException {
return DECRYPT_CACHE.getUnchecked(StringUtils.trimToEmpty(resultSet.getString(s)));
}
@Override
public Object getNullableResult(ResultSet resultSet, int i) throws SQLException {
return DECRYPT_CACHE.getUnchecked(StringUtils.trimToEmpty(resultSet.getString(i)));
}
@Override
public Object getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return DECRYPT_CACHE.getUnchecked(StringUtils.trimToEmpty(callableStatement.getString(i)));
}
}
使用方法
针对sql语句,在xml配置文件中,对需要加密的字段加上
#{name,jdbcType=VARCHAR,typeHandler=com..........DataSecurityHandler},针对查询结果,在xml配置文件中,在resultMap里面对需要解密的字段加上
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.*..........DataSecurityHandler"/>版权声明:本文为userwyh原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。