mybatis 敏感数据加密成熟解决方案 包含对旧数据的兼容处理.
使用MySQL + MyBatis + AES 数据库加密对敏感信息(姓名、身份证)存入数据库时应当需要加密,防止被恶意访问数据库时暴露信息。
·
描述整体流程
- 加密工具类实现(加密时通过前缀判断兼容已加密未加密数据) 设置秘钥
- 处理器继承重写类型处理器父类-非空存加密&非空查加密方法重写
- 实体类字段指定类型处理器
- 数据库表结构字段长度放大
- 梳理加密字段作为查询条件的需要调用加密方法
- 自己定义业务实现, 调用数据更新同步加密旧数据. 反射注解判断类属性列表中存在注解TableField(属性typeHandler instance Of EncryptHandler.class))
借鉴文档
https://sq.sf.163.com/blog/article/177903091005186048
https://sq.sf.163.com/blog/article/175742652561199104
编码应用
设置秘钥
fwc.sm4Key = "xhx$weo@810)Jo18"
加密工具类实现(加密时通过前缀判断兼容已加密未加密数据)
/**
* @Author: 达文西(俊)
* @Date: 2023/02/16
* @Description:
*/
@Component
public class SM4Util {
@Value("${fwc.sm4Key:xhx$weo@810)Jo18}")
private String sm4Key;
private static String SM4_KEY;
public static final String DEFAULT_KEY = "xhx$weo@810)Jo18";
@PostConstruct
public void getSM4_KEY() {
SM4_KEY = this.sm4Key;
}
/**
* 密文前缀
* 通过密文前缀判断内容是否被加密
*/
public static final String PREFIX = "PWD:";
/**
* SM4加密
*
* @param value
* @return
*/
public static String encrypt(String value) {
if (!StringUtils.hasText(value)) {
return value;
}
if (value.startsWith(PREFIX)) {
return value;
}
String encryptHex = null;
String key = StringUtils.hasText(SM4_KEY) ? SM4_KEY : DEFAULT_KEY;
if (key.length() != 16) {
throw new RuntimeException("密钥长度必须是16位");
}
try {
SymmetricCrypto sm4 = SmUtil.sm4(key.getBytes());
encryptHex = sm4.encryptHex(value);
} catch (Exception e) {
throw new RuntimeException("加密失败:" + e.getMessage());
}
return PREFIX + encryptHex;
}
/**
* SM4解密
*
* @param value
* @return
*/
public static String decrypt(String value) {
if (!StringUtils.hasText(value)) {
return value;
}
if (!value.startsWith(PREFIX)) {
return value;
}
String decryptStr = null;
String key = StringUtils.hasText(SM4_KEY) ? SM4_KEY : DEFAULT_KEY;
if (key.length() != 16) {
throw new RuntimeException("密钥长度必须是16位");
}
try {
SymmetricCrypto sm4 = SmUtil.sm4(key.getBytes());
value = value.substring(PREFIX.length());
decryptStr = sm4.decryptStr(value, CharsetUtil.CHARSET_UTF_8);
} catch (Exception e) {
throw new RuntimeException("解密失败:" + e.getMessage());
}
return decryptStr;
}
}
处理器继承重写类型处理器父类-非空存加密&非空查加密方法重写==>
/**
* @Author: 达文西(俊)
* @Date: 2023/02/22
* @Description: 加密入库,解密查询
*/
@MappedJdbcTypes(JdbcType.VARCHAR)
public class EncryptHandler extends BaseTypeHandler<String> {
/**
* 入库加密
* @param ps
* @param i
* @param parameter
* @param jdbcType
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
if (StringUtils.isEmpty(parameter)) {
ps.setString(i, null);
return;
}
ps.setString(i, SM4Util.encrypt(parameter));
}
/**
* 查询解密
* @param rs
* the rs
* @param columnName
* Column name, when configuration <code>useColumnLabel</code> is <code>false</code>
* @return
* @throws SQLException
*/
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return SM4Util.decrypt(rs.getString(columnName));
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
return SM4Util.decrypt(rs.getString(columnIndex));
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
return SM4Util.decrypt(cs.getString(columnIndex));
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
return SM4Util.decrypt(rs.getString(columnName));
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return SM4Util.decrypt(rs.getString(columnIndex));
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return SM4Util.decrypt(cs.getString(columnIndex));
}
}
实体类字段指定类型处理器==>
@TableField(typeHandler = EncryptHandler.class)
梳理加密字段作为查询条件,的需要调用加密方法==>
this.lambdaQuery()
.eq(PatientCardBind::getOpenUserId,openUserID)
.eq(PatientCardBind::getIdCard,SM4Util.encrypt(openIDCard))
.eq(PatientCardBind::getStatus,0)
.eq(PatientCardBind::getCardProperty, 2)
.eq(ObjectUtil.isNotEmpty(patientName), PatientCardBind::getPatientName, SM4Util.encrypt(patientName.toString()))
.list();
/**
* @Author: 达文西
* @Date: 2023/03/22
* @Description: 批量处理加密数据
*/
@Slf4j
@RestController
@RequestMapping("/encrypt")
public class EncryptedDataController {
private final EncryptedDataService encryptedDataService;
public EncryptedDataController(EncryptedDataService encryptedDataService) {
this.encryptedDataService = encryptedDataService;
}
@PostMapping("/batchProcessEncrypted")
public ResultDTO batchProcessEncryptedData() {
Boolean result = false;
try {
result = encryptedDataService.batchProcessEncryptedData();
} catch (Exception ex) {
log.error("更新加密数据失败,异常信息:{}", ex.getMessage());
if (ex instanceof BadSqlGrammarException || ex instanceof DataIntegrityViolationException) {
return new ResultDTO<>(ResultCodeEnum.SQL_FAILURE.getCode(), ResultCodeEnum.SQL_FAILURE.getMsg());
}
return new ResultDTO<>(ResultCodeEnum.OTHER_FAIL.getCode(), ResultCodeEnum.OTHER_FAIL.getMsg());
}
return new ResultDTO<>(result);
}
}
/**
* @Author: 达文西
* @Date: 2023/03/22
* @Description: 加密数据处理service层
*/
public interface EncryptedDataService {
/**
* 批量处理患者表订单表未加密数据
* @return
*/
Boolean batchProcessEncryptedData();
}
@Slf4j
@Service("encryptedDataServiceImpl")
@Transactional(rollbackFor = Exception.class)
public class EncryptedDataServiceImpl implements EncryptedDataService {
/**
* 批量处理患者表订单表未加密数据
*
* @return
*/
@Override
public Boolean batchProcessEncryptedData() {
//查询未加密的数据
List<PatientCardBind> patientCardBindList = new ArrayList<>();
this.updatePatientUnencrypted(patientCardBindList);
int patientResult = 1;
serviceImpl.updateBatch(patientCardBindList);
if (patientCardBindList.size() > 0) {
log.info("更新加密用户数据共【{}】条", patientCardBindList.size());
}
return patientResult > 0;
}
/**
* 更新患者未加密数据
*
* @param patientCardBindList
*/
private void updatePatientUnencrypted(List<PatientCardBind> patientCardBindList) {
for (PatientCardBind patientCardBind : patientCardBindList) {
//判断源数据是否需要加密,如果未加密则进行加密否则不做更改
Object obj = patientCardBind;
patientCardBind = (patientCardBind) reflectionSettingSncrypted(obj);
}
}
}
反射判断属性列表中存在注解TableField ,进行加密赋值
Object obj = tradeOrder;
tradeOrder = (TradeOrder) reflectionSettingSncrypted(obj);
private Object reflectionSettingSncrypted(Object obj) throws IllegalAccessException {
Field[] declaredFields = obj.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
TableField annotation = declaredField.getAnnotation(TableField.class);
Class<? extends TypeHandler> aClass = annotation.typeHandler();
Class<? extends TypeHandler> bClass = EncryptHandler.class;
//这里对比可能有些问题
if (aClass == bClass) {
String field_value = SM4Util.encrypt(declaredField.get(obj).toString());
declaredField.set(obj,field_value);
}
}
return obj;
}
更多推荐
已为社区贡献1条内容
所有评论(0)