日志脱敏

public class SensitiveDataConverter extends MessageConverter {

    /**
     * 日志脱敏开关
     */
    private String                 allowRun;
    /**
     * 日志脱敏关键字
     */
    private Set<SensitiveDataRule> sensitiveDataRules;

    @Override
    public String convert(ILoggingEvent event) {
        if (getAllowRun() == null) {
            Context context = getContext();
            String allowRun = context.getProperty("SensitiveDataAllowRun");
            setAllowRun(StringUtils.defaultIfBlank(allowRun, "true"));

            String[] keys = { "SensitiveDataKeys", "SensitiveDataKeys1", "SensitiveDataKeys2", "SensitiveDataKeys3" };
            Set<SensitiveDataRule> sensitiveDataRulesInit = new LinkedHashSet<SensitiveDataRule>();
            for (String key : keys) {
                String dataKeys = context.getProperty(key);
                List<SensitiveDataRule> sensitiveDataRules = JSON.parseArray(dataKeys, SensitiveDataRule.class);
                if (CollectionUtils.isNotEmpty(sensitiveDataRules)) {
                    sensitiveDataRulesInit.addAll(sensitiveDataRules);
                }
            }
            setSensitiveDataRules(sensitiveDataRulesInit);
        }

        // 获取原始日志
        String formattedMessage = event.getFormattedMessage();
        if (!"true".equals(allowRun) || sensitiveDataRules == null || sensitiveDataRules.isEmpty()
                || StringUtils.isEmpty(formattedMessage)) {
            return formattedMessage;
        }

        // 获取脱敏后的日志
        return filterMessage(formattedMessage);
    }

    /**
     * 处理日志字符串,返回脱敏后的字符串
     *
     * @param message 原始日志字符串
     * @return 脱敏后的字符串
     */
    public String filterMessage(String message) {
        StringBuilder temp = new StringBuilder(message);
        for (SensitiveDataRule sensitiveDataRule : sensitiveDataRules) {
            String key = sensitiveDataRule.getFieldName();
            int index = -1;
            do {
                index = temp.indexOf(key, index + 1);
                if (index != -1) {
                    // 判断key是否为单词字符
                    if (isWordChar(temp, key, index)) {
                        continue;
                    }
                    // 寻找值的开始位置
                    int valueStart = getValueStartIndex(temp, index + key.length());
                    // 查找值的结束位置(逗号,分号)
                    int valueEnd = getValueEndEIndex(temp, valueStart);

                    // 对获取的值进行脱敏
                    String value = temp.substring(valueStart, valueEnd);
                    String replace = SensitiveProcessUtils.shield(sensitiveDataRule.getFormat(), value);
                    temp.replace(valueStart, valueEnd, replace);
                }
            } while (index != -1);
        }
        return temp.toString();
    }

    /**
     * 判断从 {@code msg} 中获取的key值是否为单词,
     *
     * @param msg 完整字符串内容
     * @param keyword 检查的关键词
     * @param index 为key在msg中的索引值
     * @return true/false
     */
    private boolean isWordChar(CharSequence msg, String keyword, int index) {
        // 必须确定key是一个单词
        if (index != 0) {
            // 判断key前面一个字符
            char pre = msg.charAt(index - 1);
            if ((pre > 47 && pre < 58) || (pre > 64 && pre < 91) || (pre > 96 && pre < 123)) {
                // 0-9 , A-Z, a-z
                return true;
            }

            //避免 "service_name":"ABC"这种被name规则替换, 关键字的前一个字符是双引号、单引号、逗号、空格等时, 才认为后面是value需要替换
            if (!" ".equals(String.valueOf(pre)) && !"\"".equals(String.valueOf(pre))
                    && !"'".equals(String.valueOf(pre)) && !",".equals(String.valueOf(pre))
                    && !",".equals(String.valueOf(pre))) {
                return true;
            }
        }
        // 判断key后面一个字符
        char next = msg.charAt(index + keyword.length());
        if ((next > 47 && next < 58) || (next > 64 && next < 91) || (next > 96 && next < 123)) {
            // 0-9 , A-Z, a-z
            return true;
        }
        //避免 com.zhongan.mobile.bean {"xxx":"1111"}这种被PHONE_NO规则替换, 关键字的下一个字符是=:" '等时, 才认为后面是value需要替换
        if (!"=".equals(String.valueOf(next)) && !":".equals(String.valueOf(next)) && !":".equals(String.valueOf(next))
                && !" ".equals(String.valueOf(next)) && !"\"".equals(String.valueOf(next))
                && !"'".equals(String.valueOf(next))) {
            return true;
        }
        return false;
    }

    /**
     * 获取value值的开始位置
     *
     * @param msg 要查找的字符串
     * @param valueStart 查找的开始位置
     * @return value值的开始位置
     */
    private int getValueStartIndex(CharSequence msg, int valueStart) {
        // 寻找值的开始位置
        do {
            char c = msg.charAt(valueStart);
            if (c == ':' || c == '=' || c == ':') {
                // key与 value的分隔符
                valueStart++;
                c = msg.charAt(valueStart);
                if (c == '"' || c == ' ') {
                    valueStart++;
                    c = msg.charAt(valueStart);
                    if (c == '"' || c == ' ') {
                        valueStart++;
                    }
                }
                // 找到值的开始位置
                break;
            } else {
                valueStart++;
            }
        } while (true);
        return valueStart;
    }

    /**
     * 获取value值的结束位置
     *
     * @param msg 要查找的字符串
     * @param valueEnd 查找的开始位置
     * @return value值的结束位置
     */
    private int getValueEndEIndex(CharSequence msg, int valueEnd) {
        do {
            if (valueEnd == msg.length()) {
                break;
            }
            char c = msg.charAt(valueEnd);

            if (c == '"') {
                // 引号时,判断下一个值是结束,分号还是逗号决定是否为值的结束
                if (valueEnd + 1 == msg.length()) {
                    break;
                }
                char next = msg.charAt(valueEnd + 1);
                if (next == ';' || next == ',' || next == '}' || next == ' ') {
                    break;
                } else {
                    valueEnd++;
                }
            } else if (c == ';' || c == ',' || c == '}') {
                break;
            } else {
                valueEnd++;
            }

        } while (true);
        return valueEnd;
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值