业务背景:用户插入数据时,敏感字段要加密入库;展示在前端时是解密后的。
但是加密后字段只能精确筛选了。
加密解密工具类
使用的是SM4国密算法
导入依赖:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<version>1.58</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.58</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>crypto-sm</artifactId>
<version>1.1.2.RELEASE</version>
</dependency>
工具类:
public class SMUtil {
private SMUtil(){
}
private static Logger logger = LoggerFactory.getLogger("SMUtil");
private static final byte[] SECRET_KEY = StringUtils.getBytesUtf8("b#UHHQyQTTkGAe08");//带偏移量的密钥和偏移量似乎是固定16位长度的
private static final byte[] IV = StringUtils.getBytesUtf8("ivUHMQJQKKkGAei8");
private static final String SMENCRYPT_PRE = "SMENCRYPT#";
/**
* 将明文字符串加密成密文后,用Base64编码,并加上SMENCRYPT#作为前缀
* @param plainStr 明文字符串
* @return Base64编码后的密文,并且加上 SMENCRYPT# 前缀
*/
public static String encrypt(String plainStr){
if(org.apache.commons.lang3.StringUtils.isEmpty(plainStr) || isEncryptBySMCrypto(plainStr)==true){
return plainStr;
}
String encResult = null;
try {
byte[] encBytes = SM4Utils.encrypt(plainStr.getBytes("UTF-8"), SECRET_KEY ,IV);
encResult = SMENCRYPT_PRE + Base64.encodeBase64String(encBytes);
} catch (UnsupportedEncodingException e) {
logger.error("使用国密算法异常:", e);
}
return encResult;
}
/**
* 国密算法解密,要有前缀SMENCRYPT#
* @param encStr 加密字符串,要有前缀SMENCRYPT#,表示是通过国密算法加密的
* @return
*/
public static String decrypt(String encStr){
if(org.apache.commons.lang3.StringUtils.isEmpty(encStr) || isEncryptBySMCrypto(encStr) == false){
return encStr;
}
String decResult = null;
String removePre = org.apache.commons.lang3.StringUtils.remove(encStr, SMENCRYPT_PRE);
try {
byte[] decBytes = SM4Utils.decrypt(Base64.decodeBase64(removePre), SECRET_KEY);
decResult = new String(decBytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
logger.error("使用国密算法异常:", e);
}
return decResult;
}
/**
* 判断是否用国密算法加密过,通过判断前缀是否有SMENCRYPT#
* @return 有SMENCRYPT#为true表示用了国密算法加密了
*/
public static boolean isEncryptBySMCrypto(String encStr){
return org.apache.commons.lang3.StringUtils.contains(encStr, SMENCRYPT_PRE);
}
}
加密解密处理器
public class SMCryptoTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, SMUtil.encrypt(s));
}
@Override
public String getNullableResult(ResultSet resultSet, String s) throws SQLException {
return SMUtil.decrypt(resultSet.getString(s));
}
@Override
public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
return SMUtil.decrypt(resultSet.getString(i));
}
@Override
public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return SMUtil.decrypt(callableStatement.getString(i));
}
}
第一步:在Mapper.xml的resultMap标签中使用,使用示例:
需要加密的字段加上typehandler属性
<resultMap id="BaseResultMap1" type="com.thunisoft.dzsjdc.repository.model.TYwSqjbxx">
<id column="c_id" jdbcType="CHAR" property="cId"/>
<result column="c_bdcrxm" jdbcType="VARCHAR" property="cBdcrxm" typeHandler="com......SMCryptoTypeHandler"/>
<result column="c_bdcrsf" jdbcType="VARCHAR" property="cBdcrsf" typeHandler="com......SMCryptoTypeHandler"/>
<result column="c_bdcrzw" jdbcType="VARCHAR" property="cBdcrzw" typeHandler="com......SMCryptoTypeHandler"/>
<result column="c_saay" jdbcType="VARCHAR" property="cSaay"/>
<result column="c_bwtdwid" jdbcType="VARCHAR" property="cBwtdwid"/>
</resultMap>
第二步:实体类的对应敏感字段上加上注解@ColumnType,示例如下:
@Column(name = "c_bdcrxm")
@ApiModelProperty("...")
@ColumnType(typeHandler = SMCryptoTypeHandler.class)
private String cBdcrxm;
第三步:敏感字段作为筛选条件的查询语句、敏感字段插入数据库时,做出如下处理:
<if test="map.bdcrxm != null and map.bdcrxm != ''">
and c_bdcrxm like
concat('%', #{map.bdcrxm,jdbcType=VARCHAR, typeHandler=com...路径...SMCryptoTypeHandler},'%')
</if>