对数据库加密是个老生常谈的问题, 一般来说都是在开发阶段就会考虑到对于一些敏感的用户信息进行加密处理, 但是有一些情况是需要对之前不需要加密的数据进行加密, 这样一来就比较麻烦了, 需要对数据库已有的数据进行加密, 为了减轻负担, 搞了一个工具类
@FieldEncrypt
加密完之后我们查询的时候还需要对数据库进行解密, 这就需要用的@FieldEncrypt来进行方便的解密, 可以使用mybatis-plus提供的(收费项), 或者可以参考我之前的文章:
使用mybatis拦截器实现字段加密解密_使用mybatis拦截器实现在插入式进行字段加密-CSDN博客
工具类
此工具类使用到了hutool中的加密工具
FieldEncryptUtil
工具类核心原理很简单就是将数据库中的数据全部查询出来, 如果大于1000条则进行分批查询, 分批处理加密再进行更新插入, 为了方便调用, 工具类使用了反射的原理来加密更新需要进行加密的字段
@Component
@Slf4j
public class FieldEncryptUtil {
private final MybatisBatchUtils mybatisBatchUtils;
public FieldEncryptUtil(MybatisBatchUtils mybatisBatchUtils) {
this.mybatisBatchUtils = mybatisBatchUtils;
}
public <T> void updateEncryptField(Class<? extends BaseMapper<T>> mapperClass, Class<T> entityClass, Collection<T> entityList, String... field) {
entityList.forEach(entity -> {
for (String s : field) {
//通过反射获取entity中的属性值
String value = (String) ReflectUtil.getFieldValue(entity, s);
//将字段加密
if (StringUtils.isNotBlank(value) && !StringUtils.equals("null", value)) {
//使用sm4对称加密算法进行加密
String encrypt = CryptoUtil.sm4Encrypt(value, "sm4算法key");
//将加密后的值设置到entity中
ReflectUtil.setFieldValue(entity, s, encrypt);
}
}
});
mybatisBatchUtils.updateBatchById(mapperClass, entityClass, entityList);
}
public <T> void updateEncryptField(Class<? extends BaseMapper<T>> mapperClass, Class<T> entityClass, String... field) {
//获取表名
TableInfo tableInfo = TableInfoHelper.getTableInfo(entityClass);
String tableName = tableInfo.getTableName();
try {
log.info("开始更新[{}]表", tableName);
//查询所有数据
SqlSession sqlSession = SqlHelper.sqlSession(entityClass);
BaseMapper<T> mapper = SqlHelper.getMapper(entityClass, sqlSession);
Long l = mapper.selectCount(null);
//如果数据量大于1000条,分批处理
if (l > 1000) {
int pageSize = 1000;
int pageNum = (int) (l / pageSize + 1);
for (int i = 1; i <= pageNum; i++) {
log.info("开始更新[{}]表第{}页", tableName, i);
List<T> list = mapper.selectList(Wrappers.<T>query().last("limit " + pageSize + " offset " + (pageSize * (i - 1))));
updateEncryptField(mapperClass, entityClass, list, field);
}
} else {
//小于1000条,直接更新
Collection<T> entityList = mapper.selectList(null);
updateEncryptField(mapperClass, entityClass, entityList, field);
}
log.info("更新[{}]表成功", tableName);
} catch (Exception e) {
log.error("更新[{}]表失败,报错信息:[{}]", tableName, e.getMessage(), e);
throw new RuntimeException("更新" + tableName + "失败");
}
}
}
MybatisBatchUtils
public class MybatisBatchUtils {
/**
* 每次处理1000条
*/
private static final int BATCH_SIZE = 1000;
protected Log log = LogFactory.getLog(getClass());
private SqlSessionFactory sqlSessionFactory;
public MybatisBatchUtils(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
/**
* 批量保存
*
* @param mapperClass 实体对应的mapper的class
* @param entityClass 批量保存实体的class
* @param entityList 实体列表
* @param batchSize 批量保存大小
* @param <T> 实体类型
* @return 是否成功
*/
@Transactional(rollbackFor = Exception.class)
public <T> boolean saveBatch(Class<? extends BaseMapper<T>> mapperClass, Class<T> entityClass, Collection<T> entityList, int batchSize) {
String sqlStatement = SqlHelper.getSqlStatement(mapperClass, SqlMethod.INSERT_ONE);
return SqlHelper.executeBatch(entityClass, log, entityList, batchSize, (sqlSession, entity) -> sqlSession.insert(sqlStatement, entity));
}
@Transactional(rollbackFor = Exception.class)
public <T> boolean saveBatch(Class<? extends BaseMapper<T>> mapperClass, Class<T> entityClass, Collection<T> entityList) {
return saveBatch(mapperClass, entityClass, entityList, BATCH_SIZE);
}
/**
* 批量更新
*
* @param mapperClass 实体对应的mapper的class
* @param entityClass 批量保存实体的class
* @param entityList 实体列表
* @param batchSize 批量保存大小
* @param <T> 实体类型
* @return 是否成功
*/
@Transactional(rollbackFor = Exception.class)
public <T> boolean updateBatchById(Class<? extends BaseMapper<T>> mapperClass, Class<T> entityClass, Collection<T> entityList, int batchSize) {
String sqlStatement = SqlHelper.getSqlStatement(mapperClass, SqlMethod.UPDATE_BY_ID);
return SqlHelper.executeBatch(entityClass, log, entityList, batchSize, (sqlSession, entity) -> {
MapperMethod.ParamMap<T> param = new MapperMethod.ParamMap<>();
param.put(Constants.ENTITY, entity);
sqlSession.update(sqlStatement, param);
});
}
@Transactional(rollbackFor = Exception.class)
public <T> boolean updateBatchById(Class<? extends BaseMapper<T>> mapperClass, Class<T> entityClass, Collection<T> entityList) {
return updateBatchById(mapperClass, entityClass, entityList, BATCH_SIZE);
}
/**
* 批量处理修改或者插入
*
* @param data 需要被处理的数据
* @param mapperClass Mybatis的Mapper类
* @param function 自定义处理逻辑
* @return int 影响的总行数
* 使用样例
* mybatisBatchUtils.batchUpdateOrInsert(list, TestGeometryMapper.class, (e, m) -> m.insert(e));
*/
public <T, U> int batchUpdateOrInsert(List<T> data, Class<U> mapperClass, BiConsumer<T, U> function) {
int i = 1;
SqlSession batchSqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
try {
U mapper = batchSqlSession.getMapper(mapperClass);
int size = data.size();
for (T element : data) {
function.accept(element, mapper);
if ((i % BATCH_SIZE == 0) || i == size) {
batchSqlSession.flushStatements();
}
i++;
}
// 非事务环境下强制commit,事务情况下该commit相当于无效
batchSqlSession.commit(!TransactionSynchronizationManager.isSynchronizationActive());
} catch (Exception e) {
batchSqlSession.rollback();
throw e;
} finally {
batchSqlSession.close();
}
return i - 1;
}
}
使用方法
只需要调用工具类的方法, 然后入参分别为, Mapper, Entity, 需要进行加密的字段名(实体内的字段名)
/**
* ab_contact_history
*/
public void updateAbContactHistory() {
fieldEncryptUtil.updateEncryptField(AbContactHistoryMapper.class, AbContactHistory.class,
"name", "telephone", "phone", "cardId", "age", "sex");
}
3845

被折叠的 条评论
为什么被折叠?



