项目负责人原本是想把敏感数据加密放到项目收尾的时候做的。但是在项目中后期的时候,来了网警来查看数据库信息。发现数据库没有加密然后要求我们该项目要立刻把敏感数据(例如:手机号、身份证号、居住地址、)加密
public class AESEncryptionTypeHandler extends BaseTypeHandler<String> {
private SecretKeySpec secretKeySpec;
private IvParameterSpec ivParameterSpec;
public AESEncryptionTypeHandler() {
this(
// 256 bits 密钥(自己设定)
Optional.ofNullable(System.getenv("DB_ENCRYPTION_SECRET_KEY")).orElse("密钥"),
// 128 bits 初始向量(自己设定)
Optional.ofNullable(System.getenv("DB_ENCRYPTION_IV")).orElse("初始向量")
);
}
//设置密钥规格 以及 参数说明
public AESEncryptionTypeHandler(String secretKey, String iv) {
if (secretKey != null) {
secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), "AES");
ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
}
}
// 数据加密
public String encrypt(String plainText) throws GeneralSecurityException {
if (secretKeySpec == null) {
return plainText;
}
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return Base64Utils.encodeToString(encrypted);
}
// 数据解密
public String decrypt(String encrypted) throws GeneralSecurityException {
if (secretKeySpec == null) {
return encrypted;
}
//有坑 =》 如果数据加密后过长 会自动换行 导致解密报错 ->去掉空格和换行
encrypted = encrypted.replace("\n","");
encrypted = encrypted.replace("\r","");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec);
byte[] decryptedBytes = cipher.doFinal(Base64Utils.decodeFromString(encrypted));
return new String(decryptedBytes, StandardCharsets.UTF_8);
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
try {
ps.setString(i, parameter == null ? null : encrypt(parameter));
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
private String decryptValue(String value) {
if (value == null) {
return null;
}
try {
return decrypt(value);
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
实体类中表名中添加注解并设置autoResultMap=true
@TableName(value = "logininfo",autoResultMap = true)
在需要加密字段上添加注解
@TableField(value = "phone",typeHandler = AESEncryptionTypeHandler.class)
private String phone;
如果是该原本实体类的映射只需要在实体类与表的映射Map中添加typeHanlder
<result property="phone" column="userName" typeHandler="com.jeethink.encryption.AESEncryptionTypeHandler"/>
如果查询SQL中是自己定义的映射对象就需要自己定义该映射对象的ResultMap
<resultMap id="LoginInfoVOMap" type="com.jeethink.user.domain.vo.LoginInfoVO">
<result property="id" column="id"/>
<result property="userName" column="userName" typeHandler="com.jeethink.encryption.AESEncryptionTypeHandler"/>
<result property="phone" column="phone" typeHandler="com.jeethink.encryption.AESEncryptionTypeHandler"/>
</resultMap>
注意:当加密之后加密字段的查询不能模糊查询了并且在加密数据查询时加密条件需要加上typeHandler
phone = #{phone,typeHandler=com.jeethink.encryption.AESEncryptionTypeHandler}
运用Mybatis的加密相对来说更简单但是会影响查询效率如果数据量比较大建议还是考虑其他加密方法;
还有一个缺陷是这个加密方法当遇到明文或者密钥不同的数据时,会直接报错。
SET block_encryption_mode = 'aes-256-cbc'; //执行加密解密时 必须先执行该语句
SELECT TO_BASE64(AES_ENCRYPT('1234567489', '密钥', '初始向量')),AES_DECRYPT(FROM_BASE64('27ww+0zOkvlwRHihPsL3yw=='), '密钥', '初始向量');