当前位置: 首页 > article >正文

使用 Regex 在 Java 中使用 Logstash LogBack 屏蔽日志

在当今数据驱动的世界中,数据安全至关重要。日志记录框架在应用程序监控和调试中起着至关重要的作用,但它们可能会无意中暴露本不应该暴露的敏感信息。日志掩码是一种可以有效地混淆日志消息中的敏感数据以保护机密信息的技术。

了解 Logback

Logback 是 Java 应用程序中功能强大且最常用的日志记录框架。它提供灵活的配置选项,包括将日志事件格式化为 JSON 对象的能力。它是 Log4j 框架的继任者,由于其功能和易用性而迅速流行起来。它由 Logger、Encoders、Layout、Appender、Encoder 组成。

记录:Logger 是日志消息的上下文。应用程序将与此类交互以创建日志消息。

编码:编码器在 0.9.91 中引入,负责将事件转换为字节数组以及将该字节数组写出为 .作为 Layouts 引入的编码器只能将事件转换为 String,从而将其范围限制为非二进制输出。logbackOutputStream

布局:布局负责根据用户的意愿格式化 logging request,而 appender 负责将格式化的输出发送到其目的地。

附加程序:用 logback 的话来说,输出目的地称为 appender。这会将日志消息放置在其最终目标中。一个 Logger 可以有多个 Appender。目前,存在用于控制台、文件、远程套接字服务器、MySQL、PostgreSQL、Oracle 和其他数据库、JMS 和远程 UNIX Syslog 守护进程的附加程序。

关于 Logstash Logback Encoder

logstash-logback-encoder库是增强 Spring Boot 应用程序的日志记录功能的宝贵工具。它提供了一种将日志消息格式化为结构化 JSON 格式的便捷方法,使其易于 Logstash 等日志聚合和分析工具使用。JSON 格式提供了一种结构化和机器可读的方式来记录信息,使其成为高级日志分析和安全措施的理想选择。Logstash 的优势

  • JSON CustomizationLogstash 允许您自定义 JSON 输出以包含特定字段和元数据。

  • Dynamically Fields它还允许 动态添加字段 以根据应用程序上下文记录事件。

  • Improved ReadabilityJSON 格式为日志事件提供了清晰易读的结构。

  • Enhanced Search and Analysis日志聚合工具可以轻松解析和查询 JSON 日志。

  • Machine ParsingJSON 日志非常适合自动分析和警报系统。

屏蔽日志中数据的解决方案

这里的主要目标是提供一种解决方案来屏蔽数据,该解决方案在运行时是可定制的和可配置的。

这是我们的简单要求:

  1. 在日志中完全屏蔽密码。
  2. 屏蔽 Log 中除最后 5 个之外的电话号码和登录名。

步骤 1
创建 Spring Boot 应用程序。此解决方案适用于任何基于 Java 的应用程序,几乎不需要自定义。

步骤 2
配置所有正则表达式以屏蔽数据。请记住,正则表达式在资源利用方面成本高昂。确保你正在调整你的正则表达式。正则表达式组将允许我们从字符串中选择所需的子字符串。

props 配置

步骤 3
创建一个类并实现 。这个接口来自 logstash,允许我们在打印到 appender 之前自定义消息。 method 将为每个日志消息调用。MessageJsonProviderwriteTo

  • in 方法读取所有正则表达式并准备包含所有 .此方法 from 和 just 将启动的进程标记为 true。start()LogMaskerMaskingRuleAbstractJsonProvider

  • MaskingRule将保存正则表达式模式和一个函数。此函数将替换日志中标识的字符串 from。

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>@Data
public class MaskingMessagingProvider extends MessageJsonProvider {

    public static final String DEFAULT_RULES_DELIMITER = ",";
    private LogMasker logMasker;
    private String rules;

    public MaskingMessagingProvider() {
        super();
    }

    @Override
    public void start() {
        super.start();
        this.logMasker = LogMasker.create(StringUtils.tokenizeToStringArray(rules, DEFAULT_RULES_DELIMITER));
    }

    @Override
    public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException {

        if (isStarted()) {
            JsonWritingUtils.writeStringField(generator, getFieldName(), logMasker.mask(event.getFormattedMessage()));
        }
    }
}

class LogMasker {

    private MaskingRule[] masks;

    public LogMasker(MaskingRule[] masks) {
        super();
        this.masks = masks.clone();
    }

    public static LogMasker create(String[] rules) {

        return new LogMasker(Arrays.stream(rules).map(rule -> MaskingRule.create(rule)).toArray(MaskingRule[]::new));
    }

    public String mask(String input) {
        String transformed = input;
        for (MaskingRule m : masks) {
            transformed = m.mask(transformed);
        }
        return transformed;
    }
}

class MaskingRule {
    public static final int REG_EX_DEFAULT_GROUP_SELECTOR = 2;
    public static final String DEFAULT_REPLACEMENT = "*";

    private Pattern pattern;
    private UnaryOperator<String> replacement;

    public MaskingRule(Pattern maskPattern, UnaryOperator<String> replacement) {
        super();
        this.pattern = maskPattern;
        this.replacement = replacement;
    }

    public static MaskingRule create(String rule) {
        return new MaskingRule(Pattern.compile(rule), (in) -> MaskingRule.maskDataWithReplacement(in, DEFAULT_REPLACEMENT));
    }

    public String mask(String transformed) {
        Matcher matcher = pattern.matcher(transformed);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, replacement.apply(getDataToBeMasked(matcher)));
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    private static String maskDataWithReplacement(String input, String replacement) {
        int repetition = !StringUtils.hasLength(input) ? 0 : input.length();
        return String.join("", Collections.nCopies(repetition, replacement));
    }

    private static String getDataToBeMasked(Matcher matcher) {
        if (matcher.groupCount() > 1) {
            return matcher.group(REG_EX_DEFAULT_GROUP_SELECTOR);
        }
        return matcher.groupCount() > 0 ? matcher.group(1) : "";
    }
}
</code></span></span>

步骤 4 在 logback-spring.xml 文件中
配置类。

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><configuration>
    <springProperty scope="context" name="rules" source="app.logging.masking.rules"
                    defaultValue=""/>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
            <providers>
                <provider class="com.daya.logging.logstash.MaskingMessagingProvider">
                    <rules>${rules}</rules>
                    <rulesDelimiter>${rulesDelimiter}</rulesDelimiter>
                    <ruleDelimiter>${ruleDelimiter}</ruleDelimiter>
                </provider>
                <threadName/>
                <timestamp/>
                <logLevel/>
                <loggerName/>
                <mdc/>
                <version/>
                <stackTrace/>
            </providers>
        </encoder>
    </appender>
    <root level="INFO">
        <appender-ref ref="CONSOLE"/>
    </root>
</configuration>
</code></span></span>

步骤 5
运行应用程序。为简单起见,我采用了一个保存数据的字符串,并在应用程序启动时打印它。

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>@SpringBootApplication
@Slf4j
public class LogDataMaskingApplication {

    public static void main(String[] args) {
        SpringApplication.run(LogDataMaskingApplication.class, args);
        LogDataMaskingApplication.maskingTest();
    }

    public static void maskingTest() {
        String data = "{\"loginName\":\"maskingtest\",\"phoneNumber\":\"9898981212\",\"password\":\"Masking@123\"}";
        log.info(data);
    }

}
</code></span></span>

对数掩码

 


http://www.kler.cn/a/391085.html

相关文章:

  • 网站小程序app怎么查有没有备案?
  • idea 解决缓存损坏问题
  • 1小时构建Vue3知识体系之vue的生命周期函数
  • AI 扩展开发者思维方式:以 SQL 查询优化为例
  • 【Spring】@Autowired与@Resource的区别
  • 10款PDF翻译工具的探索之旅:我的使用经历与工具特色!!
  • 群控系统服务端开发模式-应用开发-前端个人资料开发
  • 基于51单片机俄罗斯方块游戏—可暂停
  • 一文了解git TAG
  • 通过scrapy和Django登录、爬取和持久化数据
  • 如何使用 SSH 连接并管理你的 WordPress 网站
  • 鸿蒙进阶篇-剩余和展开、简单和复杂类型
  • 丹摩征文活动 | SD3+ComfyUI的图像部署实践
  • linux安装zookeeper和kafka集群
  • C++学习笔记----11、模块、头文件及各种主题(一)---- 模板概览与类模板(7)
  • 设计模式之单列模式(7种单例模式案例,Effective Java 作者推荐枚举单例模式)
  • 城镇住房保障:SpringBoot系统架构解析
  • 科技前沿:汽车智能玻璃,开启透明显示新纪元
  • 【二叉树】——
  • 人保财险(外包)面试分享
  • UI资源分包 -- 基于Xasset框架代码实例
  • Ubuntu中以root身份运行Qt创建的项目
  • UML概述、类图关系及连接线表示
  • 【MQTT】代理服务比较RabbitMQ、Mosquitto 和 EMQX
  • MySQ怎么使用语法介绍(详细)
  • 工业主板在汽车制造中的应用