Mybatis使用TypeHandler实现数据的加解密转换
参考:
MyBatis之TypeHandler: https://www.cnblogs.com/yulinfeng/p/5991170.html
前段时间收到这么个需求:为安全起见,要求在数据库里保存的所有敏感信息(电话号码、email、身份证号码等等)都得加密。要是全都在java代码里控制,那就太麻烦了。还好mbatis有TypeHandler可以很好的解决这个问题。一劳永逸。
(如上面链接,这种方法可以被用来做任何类型转换的功能。)
第一步,先自定义一个类,继承自BaseTypeHandler:
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);
}
}
其中,AES.encrypt()和AES.decrypt()分别是用AES算法实现的加解密函数。实现内容如下:
public class AES {
private static final String DEFAULT_PUBLIC_KEY = "asdfghjkloiuytre"; //内容随便,16位
public static String encrypt(String src) {
byte[] data = src.getBytes();
byte[] key = DEFAULT_PUBLIC_KEY.getBytes();
try {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
cipher.init(Cipher.ENCRYPT_MODE, seckey);// 初始化
byte[] result = cipher.doFinal(data);
return new String(result);
} catch (Exception e) {
throw new RuntimeException("encrypt fail!", e);
}
}
public static String decrypt(String src) {
byte[] data = src.getBytes();
byte[] key = DEFAULT_PUBLIC_KEY.getBytes();
try {
SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
byte[] enCodeFormat = secretKey.getEncoded();
SecretKeySpec seckey = new SecretKeySpec(enCodeFormat, "AES");
Cipher cipher = Cipher.getInstance(ConfigureEncryptAndDecrypt.AES_ALGORITHM);// 创建密码器
cipher.init(Cipher.DECRYPT_MODE, seckey);// 初始化
byte[] result = cipher.doFinal(data);
return new String(result);
} catch (Exception e) {
throw new RuntimeException("decrypt fail!", e);
}
}
}
第二步,在mapper.xml里的对应字段上加上引用:
1)在resultMap上加引用,对应从数据库里取数据时转换成JAVA对象时的解密。
<resultMap type="DemoEntity" id="demoMap">
……
<result property="mobile" column="MOBILE" jdbcType="VARCHAR" typeHandler="com.demo.mybatis.handler.AESEncryptHandler"/>
……
</resultMap>
2)在sql语句中加入引用,对应从JAVA对像想数据库传递数据的加密动作(所有用到敏感信息的地方都要加):
<insert id="add" useGeneratedKeys="true" keyProperty="id" keyColumn="id" parameterType="demoMap" flushCache="true">
insert into tbl_donate_order
(
……
mobile,
……
)
values
(
……
#{mobile,jdbcType=VARCHAR,typeHandler=com.demo.mybatis.handler.AESEncryptHandler},
……
)
</insert>
第三步,在mybatis-config.xml中注册
<typeHandlers>
<package name="com.demo.mybatis.handler"></package>
</typeHandlers>
网上大多是注册到具体的类,我注册了包名,这样在这个包下的所有自定义类都可以被typeHandler使用了。
另外,
有的时候,如果我们需要对某个类型的所有数据进行转换的时候,可以这样定义类:
如,对DECIMAL和自定义的DemoDecimal转换:
@MappedJdbcTypes(JdbcType.DECIMAL)
@MappedTypes(DemoDecimal.class)
public class AmountTypeHandler extends BaseTypeHandler<DemoDecimal> {
……
}
这样,在就可以省去第二步的大面积引用了。