
数据脱敏-通过mybatis拦截器
1. 添加脱敏拦截器
package cn.cmcc.common.datasource.config;
import cn.cmcc.common.utils.bean.ObjectUtils;
import cn.hutool.crypto.digest.DigestUtil;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import javax.annotation.PostConstruct;
import java.util.*;
/**
* @author leijiahao
* @date 2023-11-07
*/
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),})
public class DataMasInterceptor implements Interceptor {
private List<String> encryptionList;
private Set<String> targetMappers; // 存储目标 Mapper 的集合
public DataMasInterceptor(Set<String> targetMappers) {
this.targetMappers = targetMappers;
}
public static String maskChineseString(String input) {
if (input == null || input.length() <= 2 || "null".equals(input)) {
return input;
}
// 查找倒数两个汉字的索引
int lastIndex = -1;
int count = 0;
for (int i = input.length() - 1; i >= 0; i--) {
char c = input.charAt(i);
if (isChineseCharacter(c)) {
count++;
if (count == 2) {
lastIndex = i;
break;
}
}
}
// 使用 * 号替换前面的部分
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lastIndex; i++) {
sb.append("*");
}
// 保留倒数两个汉字及后面的部分
sb.append(input.substring(lastIndex));
return sb.toString();
}
public static boolean isChineseCharacter(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS || block == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS || block == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A;
}
public static double maskLonLat(double value) {
if (ObjectUtils.isEmpty(value)) {
return value;
}
Random random = new Random();
double offset = random.nextDouble() * 0.0001 + 0.0001;
return value + offset;
}
@PostConstruct
public void init() {
encryptionList = new ArrayList<>();
// TODO: 2023-11-08 设置加密字段
// encryptionList.add("cgi");
// encryptionList.add("enb_id");
// encryptionList.add("enb_name");
// encryptionList.add("enodeb_name");
// encryptionList.add("cell_name");
// encryptionList.add("cell_id");
// encryptionList.add("project_name");
// encryptionList.add("project_id");
// encryptionList.add("user_name");
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object arg1 = invocation.getArgs()[0];
// 拦截通用查询,加密字段
if ((arg1 instanceof MappedStatement)
// && targetMappers.contains(((MappedStatement) arg1).getId())
&& !encryptionList.isEmpty()
) {
Object result = invocation.proceed();
if (result instanceof ArrayList) {
ArrayList resultList = (ArrayList) result;
for (Object o : resultList) {
if (o instanceof Map) {
Map<String, Object> resultMap = (Map<String, Object>) o;
for (String key : encryptionList) {
if (resultMap.containsKey(key)) {
if (key.endsWith("name")) {
resultMap.put(key, maskChineseString(String.valueOf(resultMap.get(key))));
} else if (key.endsWith("lon") || key.endsWith("lat")) {
resultMap.put(key, maskLonLat(Double.parseDouble(String.valueOf(resultMap.get(key)))));
} else {
//MD5加密
resultMap.put(key, DigestUtil.md5Hex(String.valueOf(resultMap.get(key))
.getBytes()));
}
}
}
}
}
}
return result;
} else {
return invocation.proceed();
}
}
}
大约 2 分钟