springboot使用aop或Jackson进行数据脱敏
启动类加@EnableAspectJAutoProxy自定义注解,在实体类中使用表示被脱敏字段建立aop切面类可能这里gpt会建议你用@Pointcut("execution(public * com.xx.aop..*.get*(..))")这种方式拦截,这种我试了,拦截不住。猜测在mvc返回的时候,已经不被aop拦住了,除非手动调用。并且get方式还要user成为bean,不值当。直接拦截co
Spring Boot 可以结合 注解 + 反射 + AOP(或 Jackson 序列化) 来实现数据脱敏。你可以使用 自定义注解 标记需要脱敏的字段,并在数据返回时通过 AOP 或 Jackson 序列化 进行处理
注意事项:项目中使用jackson,是无法细粒度控制在列表接口脱敏,详情接口不脱敏的。如果想要这样使用,使用多个vo区分列表和详情。
如使用aop脱敏,只在特定类中对实体类字段进行脱敏,使用aop,将脱敏的包隔开。
但这2种方式都无法解决导出时数据脱敏,导出excel只能手动调用。例如:
1.aop
启动类加@EnableAspectJAutoProxy
自定义注解,在实体类中使用表示被脱敏字段
建立aop切面类
//这里是上面的maskdata方法的复杂版,因为项目中的对象都是嵌套包裹的
private void desensitizeFields( Object obj) throws IllegalAccessException {
if (obj == null) return;
// 如果对象是一个Map类型
if (obj instanceof Map<?, ?>) {
Map<?, ?> map = (Map<?, ?>) obj;
for (Map.Entry<?, ?> entry : map.entrySet()) {
Object value = entry.getValue();
if (value != null) {
// 递归处理Map中的对象
desensitizeFields(value);
}
}
return;
}
// 如果对象是一个List类型
if (obj instanceof List<?>) {
List<?> list = (List<?>) obj;
for (Object item : list) {
if (item != null) {
// 递归处理List中的每个对象
desensitizeFields(item);
}
}
return;
}
// 处理对象的字段
Class<?> clazz = obj.getClass();
List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
clazz = clazz.getSuperclass();
if (clazz!=null){
fields.addAll(Arrays.asList(clazz.getDeclaredFields()));
}
for (Field field : fields) {
if (Modifier.isFinal(field.getModifiers())) {
continue;
}
field.setAccessible(true);
Object valueOrigin = field.get(obj);
if (valueOrigin!=null&&field.isAnnotationPresent(SensitiveData.class)) {
SensitiveData annotation = field.getAnnotation(SensitiveData.class);
// 注意:这里需要设置field为可访问
// 根据字段类型执行不同的脱敏逻辑
SensitiveTypeEnum sensitiveTypeEnum = annotation.value();
String value = valueOrigin.toString();
switch (sensitiveTypeEnum) {
case COMMON:
value = MsgDesensitizedUtil.commonStr(value);
break;
case ID_CARD:
value = MsgDesensitizedUtil.idCardNum(value);
break;
case PHONE_NUMBER:
value = MsgDesensitizedUtil.mobilePhone(value);
break;
case EMAIL:
value = MsgDesensitizedUtil.email(value);
break;
default:
throw new RuntimeException("未知脱敏类型");
}
field.set(obj, value);
} else if (valueOrigin != null && !isPrimitiveOrWrapper(field.getType())) {
// 如果字段是一个对象(非基本类型),递归处理
desensitizeFields(valueOrigin);
}
}
}
private boolean isPrimitiveOrWrapper(Class<?> clazz) {
return clazz.isPrimitive() ||
clazz.equals(String.class) ||
clazz.equals(Boolean.class) ||
clazz.equals(Integer.class) ||
clazz.equals(Character.class) ||
clazz.equals(Byte.class) ||
clazz.equals(Short.class) ||
clazz.equals(Double.class) ||
clazz.equals(Long.class) ||
clazz.equals(Float.class);
}
/**
* 共通脱敏
*
* @param commonStr
* @return
*/
public static String commonStr(String commonStr) {
if (StrUtil.isBlank(commonStr)) {
return "";
} else if (commonStr.length() == 11) {
return StrUtil.hide(commonStr, 3, commonStr.length() - 4);
} else if (commonStr.length() == 15 || commonStr.length() == 18) {
return StrUtil.hide(commonStr, 2, commonStr.length() - 3);
} else if (commonStr.length() == 1) {
return StrUtil.hide(commonStr, 0, commonStr.length());
}else if (commonStr.length() <= 3) {
return StrUtil.hide(commonStr, 1, commonStr.length());
} else if (commonStr.length() > 3 && commonStr.length() <= 6) {
return StrUtil.hide(commonStr, 1, commonStr.length() - 1);
} else if (commonStr.length() > 6 && commonStr.length() <= 10) {
return StrUtil.hide(commonStr, 2, commonStr.length() - 2);
} else if (commonStr.length() > 10 && commonStr.length() <= 16) {
return StrUtil.hide(commonStr, 3, commonStr.length() - 4);
} else if (commonStr.length() > 16) {
return StrUtil.hide(commonStr, 4, commonStr.length() - 5);
} else {
return StrUtil.hide(commonStr, 1, commonStr.length());
}
可能这里gpt会建议你用@Pointcut("execution(public * com.xx.aop..*.get*(..))")这种方式拦截,这种我试了,拦截不住。猜测在mvc返回的时候,已经不被aop拦住了,除非手动调用。并且get方式还要user成为bean,不值当。直接拦截controller包吧。
2.Jackson
自定义序列化类
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import java.io.IOException;
public class DesensitizeSerializer extends JsonSerializer<String>
implements ContextualSerializer {
private SensitiveType type;
private int startInclude;
private int endExclude;
public DesensitizeSerializer() {
this.type = SensitiveType.COMMON;
}
public DesensitizeSerializer(SensitiveType type) {
this.type = type;
}
@Override
public void serialize(String value, JsonGenerator gen,
SerializerProvider serializers) throws IOException, IOException {
switch (type) {
case COMMON:
gen.writeString(MsgDesensitizedUtil.commonStr(value));
break;
case ID_CARD:
gen.writeString(MsgDesensitizedUtil.idCardNum(value));
break;
case PHONE_NUMBER:
gen.writeString(MsgDesensitizedUtil.mobilePhone(value));
break;
case EMAIL:
gen.writeString(MsgDesensitizedUtil.email(value));
break;
default:
throw new RuntimeException("未知脱敏类型");
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) {
if (property != null) {
SensitiveData annotation = property.getAnnotation(SensitiveData.class);
if (annotation != null) {
this.type = annotation.value();
}
}
return this;
}
}
定义针对多种类型的脱敏枚举类
在实体字段上添加注解

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐
所有评论(0)