场景:在生产数据库中有很多的敏感信息是不能直接存储,需要先进行加密然后再存储,比如姓名、身份证、银行卡号等
<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-core -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.4</version>
</dependency>
1.首先要集成mybatis扩展的BaseTypeHandler,重写钩子方法,在方法中写入我们的加密逻辑,这样在插入以及取出数据的时候就可以做这个加解密,而外部无需感知
package com.wsf.spring.mybatis.handler2;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author wsf
* @since 20220120
*/
@Slf4j
public class ConfidentialHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
if(parameter != null){
try {
String encryptAES = EncryptionHelper.encryptAES(parameter);
ps.setString(i,encryptAES);
} catch (Exception e) {
log.error("AES加密错误",e);
}
}
}
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
try {
if (rs.getString(columnName) != null) {
return EncryptionHelper.decryptAES(rs.getString(columnName));
}
} catch (Exception e) {
log.error("AES解密错误", e);
}
return null;
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
try {
if (rs.getString(columnIndex) != null) {
return EncryptionHelper.decryptAES(rs.getString(columnIndex));
}
} catch (Exception e) {
log.error("AES解密错误", e);
}
return null;
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
try {
if (cs.getString(columnIndex) != null) {
return EncryptionHelper.decryptAES(cs.getString(columnIndex));
}
} catch (Exception e) {
log.error("AES解密错误", e);
}
return null;
}
}
加密工具类
package com.wsf.spring.mybatis.handler2;
import org.apache.shiro.codec.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
public class EncryptionHelper {
/*
* 加密用的Key 可以用26个字母和数字组成 使用AES-128-CBC加密模式,key需要为16位。
*/
private static final String key="hj7x89H$yuBI0456";
private static final String iv ="NIfb&95GUY86Gfgh";
/**
* 明文字段加密
*
* @param data 数据明文
* @return 加密数据
* @throws Exception 异常信息
*/
public static String encryptAES(String data) throws Exception {
try {
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
int blockSize = cipher.getBlockSize();
byte[] dataBytes = data.getBytes();
int plaintextLength = dataBytes.length;
if (plaintextLength % blockSize != 0) {
plaintextLength = plaintextLength + (blockSize - (plaintextLength % blockSize));
}
byte[] plaintext = new byte[plaintextLength];
System.arraycopy(dataBytes, 0, plaintext, 0, dataBytes.length);
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes()); // CBC模式,需要一个向量iv,可增加加密算法的强度
cipher.init(Cipher.ENCRYPT_MODE, keyspec, ivspec);
byte[] encrypted = cipher.doFinal(plaintext);
return EncryptionHelper.encode(encrypted).trim(); // BASE64做转码。
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 数据解密
*
* @param data 数据密文
* @return 数据明文
* @throws Exception 异常信息
*/
public static String decryptAES(String data) throws Exception {
try
{
byte[] encrypted1 = EncryptionHelper.decode(data);//先用base64解密
Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
SecretKeySpec keyspec = new SecretKeySpec(key.getBytes(), "AES");
IvParameterSpec ivspec = new IvParameterSpec(iv.getBytes());
cipher.init(Cipher.DECRYPT_MODE, keyspec, ivspec);
byte[] original = cipher.doFinal(encrypted1);
String originalString = new String(original, StandardCharsets.UTF_8);
return originalString.trim();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static String encode(byte[] byteArray) {
return new String(Base64.encode(byteArray),StandardCharsets.UTF_8);
}
public static byte[] decode(String base64EncodedString) {
return Base64.decode(base64EncodedString);
}
}
3.在需要使用到的地方指定这个handler,否则不生效
4.通过测试方法就可以看到对敏感字段进行了加密