Mybatis-TypeHandler实现字段加解密
背景
在公司的一个项目中,某表的一些字段是加密存储,需要解密返回给前端,于是我写下这么一串代码,对字段逐个解密
public class Object {
Field field;
...
public void decryptField(){
address = this.decrypt(address);
brandModule = this.decrypt(brandModule);
engineNumber = this.decrypt(engineNumber);
issueTime = this.decrypt(issueTime);
licensePlate = this.decrypt(licensePlate);
licenseType = this.decrypt(licenseType);
ownerName = this.decrypt(ownerName);
registerTime = this.decrypt(registerTime);
vehicleType = this.decrypt(vehicleType);
vin = this.decrypt(vin);
}
private String decrypt(String str){
return StringUtils.isNotBlank(str) ? AesUtils.decrypt(address) : "";
}
}
后被同时看到:“你怎么这么做加解密,你去看一下其他人怎么写的...”
遂翻找了同事的代码,了解到了Mybatis的TypeHandler。
什么是TypeHandler
Java对象的字段有对应的Java类型,数据库字段有对应的Jdbc类型。当我们存取数据时,Mybatis会做相应的映射处理,比如String->VARCHAR 、String->DateTime等,这就是TypeHandler的作用。Mybatis内置了大量的TypeHandler用于处理基础数据,详见 mybatis – MyBatis 3 | 配置 。
自定义TypeHandler
我们有时会需要对数据存取做一些特殊处理,比如“格林威治时间(GMT)转时间戳”,“List转String”等。Mybatis没有提供这些TypeHandler,我们可以自定义对应的TypeHandler。此处使用我遇到的问题“字段加解密”来做示例。
(一)编写自定义TypeHandler
/**
* 敏感数据处理 mybatis Handler
*
* BaseTypeHandler的泛型即是JavaType
* @MappedJdbcTypes 非必须,不写则Mybatis会根据JavaType使用对应的JdbcType。若指定了JdbcTypes,则只有指定的类型能被此Handler处理。
*/
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CHAR})
public class SensitiveDataHandler extends BaseTypeHandler<String> {
/**
* 字段加密
* 用于将Java类型参数设置到预处理语句PreparedStatement中
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, AesUtils.encrypt((String) parameter));
}
/**
* 字段解密
* 用于将查询结果集ResultSet的columnName列的数据转换成Java类型
*/
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
String columnValue = rs.getString(columnName);
return this.decrypt(columnValue);
}
/**
* 字段解密
* 用于将查询结果集ResultSet的第columnIndex列的数据转换成Java类型
*/
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String columnValue = rs.getString(columnIndex);
return this.decrypt(columnValue);
}
/**
* 字段解密
* 用于将存储过程CallableStatement的第columnIndex列的数据转换成Java类型
*/
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String columnValue = cs.getString(columnIndex);
return this.decrypt(columnValue);
}
/**
* 字段解密
*
*/
private String decrypt(String columnValue) {
return StringUtils.isBlank(columnValue) ? columnValue : AesUtils.decrypt(columnValue);
}
}
(二)使用自定义TypeHandler
查询时,在resultMap的字段中指定TypeHandler类路径
<resultMap id="BaseResultMap" type="com.demo.common.pojo.Object">
<id column="id" jdbcType="BIGINT" property="id" />
<result column="field" jdbcType="VARCHAR" property="claimNumber" typeHandler="com.demo.common.mybatis.SensitiveDataHandler" />
...
</resultMap>
<select id="selectById" parameterType="java.lang.Long" resultMap="BaseResultMap">
select * from "table"
where "id" = #{id,jdbcType=BIGINT}
</select>
新增/修改时,在占位符中指定TypeHandler类路径
<insert id="insert" parameterType="com.demo.common.pojo.Object">
insert into "table"
(id, field1, ...)
value
(#{id, jdbcType=BIGINT}, #{field, jdbcType=VARCHAR, typeHandler="com.demo.common.mybatis.SensitiveDataHandler"})
<insert>
此时我们就完成了对指定字段做保存时加密,查询时解密。
总结
屏幕前的程序猿:“说得好!我还是选择手写加解密 0.o”
如大家所见,使用自定义TypeHandler这种方式不一定会降低我们的工作量,需要结合实际情况来考虑。在我的项目中,不止一张表的字段需要做加解密,如果都写在业务代码中,不仅大大增加了工作量,还会降低业务逻辑的可读性。这种情况使用自定义TypeHandler绝对是会方便很多的。如果你的数据处理逻辑简单,且不会造成大量重复代码,直接写在代码中就完事了。
版权声明:
本站所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自
像柔风的个人博客!
喜欢就支持一下吧