Десенсибилизация данных — реализуйте десенсибилизацию полей журнала на основе пользовательских аннотаций Java.

Java задняя часть



Как упоминалось выше, есть две основные идеи для аллергии на данные: первая — это обработка полей, которые необходимо десенсибилизировать перед сериализацией объекта, а затем сериализовать их в обычном режиме, а вторая — десенсибилизация объекта при сериализации объекта. обрабатываются.


Идеи реализации десенсибилизации


Здесь обсуждается первый метод, который заключается в реализации десенсибилизации журнала на основе пользовательских аннотаций.

Чтобы сделать данные менее чувствительными, в основном отключите некоторые ключи и несколько полей. Например, в определенном объекте может быть отключено только одно поле пароля, поэтому вы можете использовать пользовательские аннотации. Удобно добавлять аннотацию к полю, которое необходимо десенсибилизировать .

Общая идея такова:

这里写图片描述

При написании лога перед сериализацией клонируйте копию объекта для печати, а затем узнайте поля, к которым добавляются пользовательские аннотации десенсибилизации для обработки и преобразования соответствующих правил (например, измените «Энди Лау» на «Лю* hua"), а затем сериализовать объект.

Основной код:

Определите аннотации, используемые для идентификации нечувствительных полей Desensitized.java

@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Desensitized {
    /*脱敏类型(规则)*/
    SensitiveTypeEnum type();
    /*判断注解是否生效的方法*/
    String isEffictiveMethod() default "";
}

Тип десенсибилизации SensitiveTypeEnum.java

public enum SensitiveTypeEnum {
    /** 中文名 */
    CHINESE_NAME,
    /** 身份证号 */
    ID_CARD,
    /** 座机号 */
    FIXED_PHONE,
    /** 手机号 */
    MOBILE_PHONE,
    /** 地址 */
    ADDRESS,
    /** 电子邮件 */
    EMAIL,
    /** 银行卡 */
    BANK_CARD,
    /** 密码 */
    PASSWORD;
}

Реализовать класс обработки десенсибилизации DesensitizedUtils.java

public class DesensitizedUtils {

     /**
     * 获取脱敏json串
     *
     * @param javaBean
     * @return
     */
    public static String getJson(Object javaBean) {
        String json = null;
        if (null != javaBean) {
            try {
                if (javaBean.getClass().isInterface()) return json;
                /* 克隆出一个实体进行字段修改,避免修改原实体 */
                Object clone = ObjectUtils.deepClone(javaBean);

                /* 定义一个计数器,用于避免重复循环自定义对象类型的字段 */
                Set<Integer> referenceCounter = new HashSet<Integer>();

                /* 对克隆实体进行脱敏操作 */
                DesensitizedUtils.replace(ObjectUtils.getAllFields(clone), clone, referenceCounter);

                /* 利用fastjson对脱敏后的克隆对象进行序列化 */
                json = JSON.toJSONString(clone, SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullListAsEmpty);

                /* 清空计数器 */
                referenceCounter.clear();
                referenceCounter = null;
            } catch (Throwable e) {
                e.printStackTrace();
            }
        }
        return json;
    }

    /**
     * 对需要脱敏的字段进行转化
     *
     * @param fields
     * @param javaBean
     * @param referenceCounter
     * @throws IllegalArgumentException
     * @throws IllegalAccessException
     */
    private static void replace(Field[] fields, Object javaBean, Set<Integer> referenceCounter) throws IllegalArgumentException, IllegalAccessException {
        if (null != fields && fields.length > 0) {
            for (Field field : fields) {
                field.setAccessible(true);
                if (null != field && null != javaBean) {
                    Object value = field.get(javaBean);
                    if (null != value) {
                        Class<?> type = value.getClass();
                        //处理子属性,包括集合中的
                        if (type.isArray()) {//对数组类型的字段进行递归过滤
                            int len = Array.getLength(value);
                            for (int i = 0; i < len; i++) {
                                Object arrayObject = Array.get(value, i);
                                if (isNotGeneralType(arrayObject.getClass(), arrayObject, referenceCounter)) {
                                    replace(ObjectUtils.getAllFields(arrayObject), arrayObject, referenceCounter);
                                }
                            }
                        } else if (value instanceof Collection<?>) {//对集合类型的字段进行递归过滤
                            Collection<?> c = (Collection<?>) value;
                            Iterator<?> it = c.iterator();
                            while (it.hasNext()) {
                                Object collectionObj = it.next();
                                if (isNotGeneralType(collectionObj.getClass(), collectionObj, referenceCounter)) {
                                    replace(ObjectUtils.getAllFields(collectionObj), collectionObj, referenceCounter);
                                }
                            }
                        } else if (value instanceof Map<?, ?>) {//对Map类型的字段进行递归过滤
                            Map<?, ?> m = (Map<?, ?>) value;
                            Set<?> set = m.entrySet();
                            for (Object o : set) {
                                Map.Entry<?, ?> entry = (Map.Entry<?, ?>) o;
                                Object mapVal = entry.getValue();
                                if (isNotGeneralType(mapVal.getClass(), mapVal, referenceCounter)) {
                                    replace(ObjectUtils.getAllFields(mapVal), mapVal, referenceCounter);
                                }
                            }
                        } else if (value instanceof Enum<?>) {
                            continue;
                        }

                        /*除基础类型、jdk类型的字段之外,对其他类型的字段进行递归过滤*/
                        else {
                            if (!type.isPrimitive()
                                    && type.getPackage() != null
                                    && !StringUtils.startsWith(type.getPackage().getName(), "javax.")
                                    && !StringUtils.startsWith(type.getPackage().getName(), "java.")
                                    && !StringUtils.startsWith(field.getType().getName(), "javax.")
                                    && !StringUtils.startsWith(field.getName(), "java.")
                                    && referenceCounter.add(value.hashCode())) {
                                replace(ObjectUtils.getAllFields(value), value, referenceCounter);
                            }
                        }
                    }

                    //脱敏操作
                    setNewValueForField(javaBean, field, value);

                }
            }
        }
    }

    /**
     * 脱敏操作(按照规则转化需要脱敏的字段并设置新值)
     * 目前只支持String类型的字段,如需要其他类型如BigDecimal、Date等类型,可以添加
     *
     * @param javaBean
     * @param field
     * @param value
     * @throws IllegalAccessException
     */
    public static void setNewValueForField(Object javaBean, Field field, Object value) throws IllegalAccessException {
        //处理自身的属性
        Desensitized annotation = field.getAnnotation(Desensitized.class);
        if (field.getType().equals(String.class) && null != annotation && executeIsEffictiveMethod(javaBean, annotation)) {
            String valueStr = (String) value;
            if (StringUtils.isNotBlank(valueStr)) {
                switch (annotation.type()) {
                    case CHINESE_NAME: {
                        field.set(javaBean, DesensitizedUtils.chineseName(valueStr));
                        break;
                    }
                    case ID_CARD: {
                        field.set(javaBean, DesensitizedUtils.idCardNum(valueStr));
                        break;
                    }
                    case FIXED_PHONE: {
                        field.set(javaBean, DesensitizedUtils.fixedPhone(valueStr));
                        break;
                    }
                    case MOBILE_PHONE: {
                        field.set(javaBean, DesensitizedUtils.mobilePhone(valueStr));
                        break;
                    }
                    case ADDRESS: {
                        field.set(javaBean, DesensitizedUtils.address(valueStr, 8));
                        break;
                    }
                    case EMAIL: {
                        field.set(javaBean, DesensitizedUtils.email(valueStr));
                        break;
                    }
                    case BANK_CARD: {
                        field.set(javaBean, DesensitizedUtils.bankCard(valueStr));
                        break;
                    }
                    case PASSWORD: {
                        field.set(javaBean, DesensitizedUtils.password(valueStr));
                        break;
                    }
                }
            }
        }
    }
}

Объект теста десенсибилизации UserInfo.java

public class UserInfo{
    @Desensitized(type = SensitiveTypeEnum.CHINESE_NAME)
    private String realName;

    @Desensitized(type = SensitiveTypeEnum.ID_CARD)
    private String idCardNo;

    @Desensitized(type = SensitiveTypeEnum.MOBILE_PHONE)
    private String mobileNo;

    private String account;

    @Desensitized(type = SensitiveTypeEnum.PASSWORD, isEffictiveMethod = "isEffictiveMethod")
    private String password;

    //setter、getter略
}

тестовое задание:

@Test
public void testUserInfoDesensitize() {
    BaseUserInfo baseUserInfo = new BaseUserInfo()
                .setRealName("胡丹尼")
                .setIdCardNo("158199199013141120")
                .setMobileNo("13579246810")
                .setAccount("dannyhoo123456")
                .setPassword("123456");
    System.out.println("脱敏前:" + JSON.toJSONString(baseUserInfo));
    System.out.println("脱敏后:" + DesensitizedUtils.getJson(baseUserInfo));
}

Это только часть кода, весь код обновлен на github:GitHub.com/Дэнни ХУ/…

Сложная часть всего процесса - клонирование объекта.Формат объекта журнала, который будет напечатан в реальной сцене, постоянно меняется, и также много переменных типов объекта, таких как интерфейс, перечисление, коллекция. , карта, пользовательский тип и т. д., в процессе реализации также попробуйте. Для достижения глубокого клонирования сущностей используются различные методы, такие как сначала сериализация объекта, а затем десериализация клонированного объекта или использование стороннего класса инструментов клонирования. , все из которых не очень хорошо совместимы с форматом объекта в реальной среде.Приведенный выше исходный код Редактор пересматривал его снова и снова в соответствии с существующими потребностями и после многих ошибок.Может быть много необоснованных мест.Время поджимает, и мы продолжим оптимизировать его позже.

Если у вас есть какие-либо вопросы и предложения по всей идее реализации и методу реализации, добро пожаловать на обмен и обсуждение. Если у вас есть лучший метод, надеюсь, вы поделитесь им~

Ссылка:blog.CSDN.net/Process0317/AR…


【Перепечатка, пожалуйста, укажите источник - Ху Юян«Десенсибилизация данных — реализация десенсибилизации полей журнала на основе пользовательских аннотаций Java»