sunny123456

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

java打印日志时,如何对字段进行脱敏?
原文链接:https://blog.csdn.net/weixin_43901749/article/details/129150818
第一步,创建类继承MessageConverter,重写convert方法,添加注解 @Component("sensitive")
第二步,在logback.xml中增加 conversionRule 标签

在我们开发项目的时候,有些字段比较敏感,比如用户信息,这就需要在打印日志的时候对相关字段脱敏处理,本文提供的脱敏方案是使用conversionRule标签的方式,通过继承MessageConverter,在打印日志的时候对相关字段进行脱敏。
第一步,创建类继承MessageConverter,重写convert方法,如下:

public class SensitiveMessageConverter extends MessageConverter {
    /**
     * 正则匹配模式 
     */
    public static final Pattern REGEX_PATTERN = Pattern.compile("\\s*([\"]?[\\w]+[\"]?)(\\s*[:=]+[^\\u4e00-\\u9fa5@,.*{\\[\\w]*\\s*)([\\u4e00-\\u9fa5_\\-@.\\w]+)[\\W&&[^\\-@.]]?\\s*");
    @Override
    public String convert(ILoggingEvent event) {
        // 获取原始日志
        String formattedMessage = event.getFormattedMessage();
        return doConvert(formattedMessage);
    }
    private String doConvert(String formattedMessage) {
        if (StringUtils.isBlank(formattedMessage)) return formattedMessage;
        // 获取是否脱敏开关,可分环境配置是否开启
        Boolean convertSwitch = ApolloPropertyUtil.getBooleanProperty("log.sensitive.convert.switch", true);
        // 获取需要脱敏的字段。配置模板(key为脱敏模式,value为脱敏字段,可以有多个,other不脱敏):
        // {"name":["name","userName"],"mobilePhone":["phone"],"password":["password"],"idCard":["idcard"],"email":["email"],"other":["address"]}
        JSONObject jsonObject = ApolloPropertyUtil.getJSONObjectProperty("log.sensitive.convert.fields", "");
        // 是否开启脱敏开关
        if (!convertSwitch) return formattedMessage;
        if (Objects.isNull(jsonObject)) return formattedMessage;
        Matcher matcher = REGEX_PATTERN.matcher(formattedMessage);
        while (matcher.find()) {
            // 字段
            String key = matcher.group(1);
            key = key.replaceAll("\"", "");
            // 字段值
            String value = matcher.group(3);
            String mode = "";
            Iterator<String> iterator = jsonObject.keySet().iterator();
            while (iterator.hasNext()) {
                String next = iterator.next();
                List<String> list = (List<String>) jsonObject.get(next);
                if (list.contains(key)) {
                    // 命中当前属性属于哪一种脱敏模式(只能命中一种),目前支持 phone、password、idcard、other
                    mode = next;
                    break;
                }
            }
            // 没有命中脱敏模式,或者属性值为空,或者属性值为”null“,直接返回
            if (StringUtils.isAnyBlank(value, mode) || "null".equals(value)) continue;
            String afterValue = desensitize(mode, value);
            String origin = matcher.group(1) + matcher.group(2) + matcher.group(3);
            formattedMessage = formattedMessage.replace(origin, matcher.group(1) + matcher.group(2) + afterValue);
        }
        return formattedMessage;
    }
    /**
     * 脱敏
     *
     * @param mode 脱敏模式
     * @param value 脱敏的内容
     * @return 脱敏后的内容
     */
    private String desensitize(String mode, String value) {
        SensitiveModeEnum sensitiveModeEnum = SensitiveModeEnum.get(mode);
        // 获取不到相关脱敏模式,暂不支持,返回原日志
        if (Objects.isNull(sensitiveModeEnum)) return value;
        char[] chars = value.toCharArray();
        // 具体模式的策略可以再扩展(比如可以支持指定下标范围,指定正则,指定符号前面几位或者后面几位脱敏等等),这里暂不展开
        switch (sensitiveModeEnum) {
            case NAME:
                chars[0] = '*';
                break;
            case MOBILE_PHONE:
                for (int i = 3; i < 7; i++) {
                    chars[i] = '*';
                }
                break;
            case TELEPHONE:
                for (int i = 3; i < 5; i++) {
                    chars[i] = '*';
                }
                break;
            case ID_CARD:
                for (int i = 9; i < 13; i++) {
                    chars[i] = '*';
                }
                break;
            case PASSWORD:
                Arrays.fill(chars, '*');
                break;
            case EMAIL:
                for (int i = 3; i < 6; i++) {
                    chars[i] = '*';
                }
                break;
            case OTHER:
                break;
        }
        return new String(chars);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

第二步,在logback.xml中增加 conversionRule 标签

conversionWord是输入的日志信息,converterClass对应上面增加的类

<conversionRule conversionWord="msg" converterClass="xxx.xxx.log.SensitiveMessageConverter"/>
  • 1

测试

@Test
    public void test() {
        HashMap<Object, Object> map = new HashMap<>();
        Map<String, String> map1 = new HashMap<>();
        map1.put("email", "1234567@qq.com");
        map1.put("address", "上海市详细地址");
        map.put("name", "张三分");
        map.put("sendPhone", "18912430987");
        map.put("password", "123456");
        map.put("detail", map1);
        log.info("结果:{}", JSON.toJSONString(map));
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

结果:

结果:{"password":"******","name":"*三分","sendPhone":"189****0987","detail":{"address":"上海市详细地址","email":"123***7@qq.com"}} 
  • 1
posted on 2023-08-01 17:57  sunny123456  阅读(582)  评论(0编辑  收藏  举报