springboot使用aop或Jackson进行数据脱敏

 项目中如果所有字段都脱敏,可以使用jackson。aop可以用redis做开关,也可以调整切面精确范围,将脱敏的包隔开。但这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;
    }

}

针对多种类型的脱敏枚举类

在实体中添加就行了,不需要把自定义序列化加载到SimpleModule里

Spring Boot是一个用于构建独立的、生产级别的Spring应用程序的框架,它提供了丰富的功能和特性,方便开发人员快速搭建和开发项目。 在Spring Boot使用AOP(面向切面编程)可以实现将一些公共的功能逻辑代码从业务逻辑代码中解耦出来,提高代码的复用性和可维护性。通过AOP可以在方法的前后注入一些通用的逻辑,例如日志记录、异常处理、权限校验等。 其中,使用AOP进行结果的统一封装是很常见的需求。通过AOP可以在方法执行后对返回的结果进行封装,统一处理返回结果的格式,并可以对结果进行一些统一的处理操作,例如添加统一的返回码、返回信息、返回时间等。 在Spring Boot使用AOP进行结果的统一封装可以按照以下步骤进行: 1. 创建一个切面(Aspect),通过在切面类上加上@Aspect注解标识该类是一个切面类。 2. 在切面类中定义一个切点(Pointcut),通过定义一个方法并添加@Pointcut注解来指定切入点。 3. 在切面类中定义一个通知(Advice),通过@Before、@After、@Around等注解来指定通知类型,并在通知方法中编写需要执行的逻辑。 4. 在通知方法中获取方法的返回结果,并进行相应的封装和处理。 5. 在Spring Boot的配置类中添加@EnableAspectJAutoProxy注解来启用AOP使用以上步骤可以实现对方法返回结果的统一封装,使得返回结果具有统一的格式和处理逻辑。这样可以提高代码的重用性和可维护性,并且可以在一处对结果进行集中处理,减少了代码的重复性,提高了开发效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值