背景
近期在做一个对数据安全要求比较高的软件,用户要求做到对接口、文件、以及数据库部分敏感字段进行加密。由于系统数据库中部分字段内容非常较敏感,用户要求需要对这些字段内容进行加密存储。
TypeHandler
MyBatis在设置预处理语句(PreparedStatement)中的参数,或从结果集中取出一个值时, 会选择使用对应的TypeHandler类型处理器,将获取到的值以合适的方式转换成 Java 类型。
实现
@Slf4j
@MappedJdbcTypes(JdbcType.VARCHAR)
@Component
public class EncryptTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
// 在这里进行加密
String encryptedData = encrypt(parameter);
ps.setString(i, encryptedData);
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 在这里进行解密
String encryptedData = rs.getString(columnName);
if(IrStringUtil.isBlank(encryptedData)){
return encryptedData;
}
return decrypt(encryptedData);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
// 在这里进行解密
String encryptedData = rs.getString(columnIndex);
return decrypt(encryptedData);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
// 在这里进行解密
String encryptedData = cs.getString(columnIndex);
if(IrStringUtil.isBlank(encryptedData)){
return encryptedData;
}
return decrypt(encryptedData);
}
// 加密方法
private String encrypt(String data) {
String encryptData = "";
// 实现你的加密逻辑
try{
encryptData = AesEncryptUtils.aesEncrypt(data);
encryptData = GoConstant.ENCRYPT_TYPE.AES+encryptData;
return encryptData;
}catch (Exception e){
e.printStackTrace();
log.error(e.getMessage());
}
return data;
}
// 解密方法
private String decrypt(String encryptedData) {
String decryptData = "";
// 实现你的解密逻辑
if(encryptedData.startsWith(GoConstant.ENCRYPT_TYPE.AES)){
try{
decryptData = AesEncryptUtils.aesDecrypt(encryptedData.substring(4));
return decryptData;
}catch (Exception e){
e.printStackTrace();
log.error(e.getMessage());
}
}
return encryptedData;
}
}
使用
在要加密字段上添加注解@TableField, 配置对应的typeHandler,示例如下:
@TableField(jdbcType = JdbcType.VARCHAR,typeHandler = EncryptTypeHandler.class)
private String phone;
配置好之后使用Mybatis的BaseMapperr内部的新增与修改方法时对应字段会进行加密后再存入数据库,查询时为解密之后的结果。
如果不是Mybatis-Plus的 BaseMapper内部的方法,需要我们在mapper.xml文件中resultMap或者在参数使用时显示的声明TypeHandler,如下:
<!-- 查询解密 -->
<result column="phone" property="phone" typeHandler="com.xxx.xxx.encrypt.handler.EncryptTypeHandler"/>
<!-- 修改加密 -->
<update id="updatePhone">
update user
set phone = #{phone, typeHandler=com.example.typeHandle.ClientPhoneTypeHandler}
where id = #{id}
</update>
总结
1.TypeHandler不仅能做数据库字段加解密,还能做时间格式处理、字段脱敏等。
2.数据库字段加密带来的问题:不好做模糊查询。网上有解决方案(将一个字段拆分多个存储,查询时通过加密后匹配这些字段)不过这个得看场景了