侧边栏壁纸
  • 累计撰写 102 篇文章
  • 累计创建 42 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录
AI

log-desensitize

天明
2026-07-29 / 0 评论 / 0 点赞 / 2 阅读 / 0 字 / 正在检测是否收录...
---
name: log-desensitize
description: 为Java/Spring Boot项目集成日志脱敏功能。基于Log4j2 RewriteAppender机制,在日志输出层自动 masking 敏感数据(手机号、身份证、邮箱、银行卡等)。涵盖依赖检查与升级、log4j2配置Rewrite改造、启动类注解添加、se.properties规则文件生成。当用户说"日志脱敏"、"log desensitize"、"日志mask"、"敏感数据脱敏"、"配置日志脱敏"时使用此skill。
---

# 日志脱敏集成

为 Java/Spring Boot 项目集成基于 Log4j2 RewriteAppender 的日志脱敏能力,在日志输出层自动 masking 敏感数据,无需修改业务代码。

---

## 输入格式

用户应提供以下信息(缺少时主动询问):

| 参数 | 必填 | 说明 | 示例 |
|------|------|------|------|
| 项目路径 | 是 | Maven 项目的根目录或启动模块目录 | `/path/to/nicestay-api` |
| log4j2 版本 | 否 | 默认跟随 Spring Boot BOM 管理版本 | `2.24.3` |
| 脱敏规则 | 否 | 默认包含全部内置规则,可指定子集 | `手机号,身份证,邮箱` |

---

## 执行流程

### Task 1: 检查并处理 log4j-core 依赖

**1.1 扫描项目 pom.xml**

在项目根 pom.xml 和核心模块 pom.xml 中搜索 `log4j-core` 依赖:

```xml
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
</dependency>
```

**1.2 判断处理策略**

| 场景 | 处理方式 |
|------|---------|
| 已存在且指定了版本 | 检查版本号,若非最新稳定版则更新 `<version>` |
| 已存在但版本由 BOM/parent 管理 | 不修改版本,确认版本 ≥ 2.17.1(安全版本)即可 |
| 已存在但标记为 `<optional>true</optional>` | 保留 optional 标记不变 |
| 不存在 | 在核心模块 pom.xml 的 `<dependencies>` 中添加 |

**1.3 版本安全底线**

log4j-core 版本必须 ≥ **2.17.1**(修复 Log4Shell 漏洞)。若检测到低于此版本,必须升级并警告用户。

**1.4 添加依赖模板**

若需新增依赖,添加至核心模块(如 `jeecg-boot-base-core/pom.xml`):

```xml
<!-- log4j2 core (for Log4j2 desensitization RewriteAppender) -->
<dependency>
    <groupId>org.apache.logging.log4j</groupId>
    <artifactId>log4j-core</artifactId>
    <optional>true</optional>
</dependency>
```

> 版本由 Spring Boot BOM 管理时无需显式指定 `<version>`。

---

### Task 2: 更新 log4j2 配置文件 — 为每个 Appender 添加 Rewrite

**2.1 定位或创建 log4j2 配置文件**

按优先级查找现有配置:
1. `log4j2-spring.xml`(Spring Boot 推荐)
2. `log4j2.xml`
3. 若均不存在,在启动模块 `src/main/resources/` 下创建 `log4j2-spring.xml`

**2.2 Rewrite 改造规则**

对配置文件中的**每一个**现有 Appender,执行以下改造:

**改造步骤:**
1. 将原 Appender 的 `name` 改为 `{原名}-inner`(如 `STDOUT` → `STDOUT-inner`)
2. 在原 Appender 位置上方新增一个 `RewriteAppender`,使用原名作为新 Appender 的 `name`
3. `RewriteAppender` 内部引用 `{原名}-inner` 的 AppenderRef
4. 配置 `RewritePolicy` 使用 `se.properties` 中的脱敏规则

**改造前示例:**
```xml
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
    </encoder>
</appender>
```

**改造后示例(Log4j2 格式):**
```xml
<!-- 原始 Appender 改名为 STDOUT-inner -->
<Console name="STDOUT-inner">
    <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n"/>
</Console>

<!-- 新增 RewriteAppender 使用原名 STDOUT -->
<Rewrite name="STDOUT">
    <AppenderRef ref="STDOUT-inner"/>
    <RewritePolicy>
        <!-- 脱敏规则通过 PropertiesRewritePolicy 或自定义 Plugin 加载 se.properties -->
    </RewritePolicy>
</Rewrite>
```

**2.3 同步更新所有 AppenderRef 引用**

修改 `<root>` 或 `<logger>` 中的 `appender-ref`,确保引用名称与 Rewrite 后的名称一致:

```xml
<root level="INFO">
    <appender-ref ref="STDOUT"/>     <!-- 引用 Rewrite 后的名称 -->
    <appender-ref ref="FILE"/>       <!-- 引用 Rewrite 后的名称 -->
</root>
```

**2.4 完整的 log4j2-spring.xml 模板**

若需从零创建,参考以下结构(按项目实际 Appender 增减):

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN" monitorInterval="30">
    <properties>
        <property name="LOG_HOME">../logs</property>
    </properties>

    <Appenders>
        <!-- ===== Console ===== -->
        <Console name="STDOUT-inner">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n"/>
        </Console>
        <Rewrite name="STDOUT">
            <AppenderRef ref="STDOUT-inner"/>
            <PropertiesRewritePolicy>
                <Property name="sensitive.pattern.file">${sys:user.dir}/se.properties</Property>
            </PropertiesRewritePolicy>
        </Rewrite>

        <!-- ===== Rolling File ===== -->
        <RollingFile name="FILE-inner"
                     fileName="${LOG_HOME}/app.log"
                     filePattern="${LOG_HOME}/app-%d{yyyy-MM-dd}-%i.log">
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n"/>
            <Policies>
                <SizeBasedTriggeringPolicy size="10MB"/>
                <TimeBasedTriggeringPolicy interval="1"/>
            </Policies>
            <DefaultRolloverStrategy max="30"/>
        </RollingFile>
        <Rewrite name="FILE">
            <AppenderRef ref="FILE-inner"/>
            <PropertiesRewritePolicy>
                <Property name="sensitive.pattern.file">${sys:user.dir}/se.properties</Property>
            </PropertiesRewritePolicy>
        </Rewrite>

        <!-- ===== Error HTML ===== -->
        <File name="HTML-inner" fileName="${LOG_HOME}/error-log.html">
            <ThresholdFilter level="ERROR" onMatch="ACCEPT" onMismatch="DENY"/>
            <HtmlLayout title="Error Log" locationInfo="true"/>
        </File>
        <Rewrite name="HTML">
            <AppenderRef ref="HTML-inner"/>
            <PropertiesRewritePolicy>
                <Property name="sensitive.pattern.file">${sys:user.dir}/se.properties</Property>
            </PropertiesRewritePolicy>
        </Rewrite>
    </Appenders>

    <Loggers>
        <Root level="INFO">
            <AppenderRef ref="STDOUT"/>
            <!-- 生产环境按需打开 -->
            <!-- <AppenderRef ref="FILE"/> -->
            <!-- <AppenderRef ref="HTML"/> -->
        </Root>
    </Loggers>
</Configuration>
```

> **注意**:若项目使用 logback 而非 log4j2,需要先排除 `spring-boot-starter-logging`,再添加 `spring-boot-starter-log4j2`。具体操作见下方"日志框架切换"章节。

**2.5 名称同步检查清单**

改造完成后,逐一验证:

```
Task Progress:
- [ ] 每个原 Appender 已改名为 {原名}-inner
- [ ] 每个 RewriteAppender 使用了原 Appender 的名称
- [ ] <root> 和 <logger> 中的 appender-ref 指向 Rewrite 后的名称
- [ ] 无遗漏的 appender-ref 仍指向 -inner 名称
```

---

### Task 3: 在启动类添加注解

**3.1 定位启动类**

查找项目中的 Spring Boot 启动类(含 `@SpringBootApplication` 注解的类)。

**3.2 添加日志脱敏注解**

在启动类上添加 `@EnableLogDesensitize` 注解:

```java
import org.jeecg.common.desensitization.annotation.EnableLogDesensitize;

@Slf4j
@SpringBootApplication
@EnableLogDesensitize  // 启用日志脱敏
public class JeecgSystemApplication {
    // ...
}
```

**3.3 注解定义**

若项目中不存在 `@EnableLogDesensitize` 注解,需创建:

```java
package org.jeecg.common.desensitization.annotation;

import org.springframework.context.annotation.Import;
import java.lang.annotation.*;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Import(LogDesensitizeConfig.class)
public @interface EnableLogDesensitize {
}
```

**3.4 配置类**

若项目中不存在 `LogDesensitizeConfig`,需创建:

```java
package org.jeecg.common.desensitization.config;

import org.springframework.context.annotation.Configuration;

@Configuration
public class LogDesensitizeConfig {
    // 加载 se.properties 中的脱敏规则
    // 注册 Log4j2 RewritePolicy(若需要编程式注册)
}
```

> 如果项目已通过 log4j2.xml 中的 `PropertiesRewritePolicy` 直接加载 `se.properties`,此配置类可简化为空壳,仅作为注解的 Import 目标。

---

### Task 4: 生成 se.properties 配置文件

**4.1 文件位置**

在启动模块的 `src/main/resources/` 下创建 `se.properties`。

**4.2 默认脱敏规则**

```properties
# ============================================
# 日志脱敏规则配置 (se.properties)
# 格式: sensitive.{规则名}={正则表达式}|{替换模板}
# ============================================

# 手机号脱敏: 138****1234
sensitive.mobile=(\\d{3})\\d{4}(\\d{4})|$1****$2

# 身份证号脱敏: 110***********1234
sensitive.idcard=(\\d{3})\\d{11}(\\d{4})|$1***********$2

# 邮箱脱敏: t****@example.com
sensitive.email=(.{1})[^@]*(@.*)|$1****$2

# 银行卡脱敏: 6222****1234
sensitive.bankcard=(\\d{4})\\d+(\\d{4})|$1****$2

# 中文姓名脱敏: 张*
sensitive.chinese_name=([\\u4e00-\\u9fa5]){1}([\\u4e00-\\u9fa5])+|$1*

# 地址脱敏: 保留前6个字符
sensitive.address=(.{6}).*|$1****

# 固定电话脱敏: 010****1234
sensitive.phone=(\\d{3,4})\\d+(\\d{4})|$1****$2
```

**4.3 规则格式说明**

```
sensitive.{规则名}={正则表达式}|{替换模板}
```

- **规则名**:唯一标识,用于日志标记
- **正则表达式**:Java 正则语法,用 `()` 分组捕获
- **替换模板**:`$1`、`$2` 引用正则分组,其余字符原样输出
- 规则按声明顺序依次匹配,互不干扰

**4.4 自定义规则扩展**

用户可按需添加新规则,格式同上。例如:

```properties
# 自定义: 订单号脱敏(保留前4后4)
sensitive.order_no=(.{4}).+(.{4})|$1****$2
```

---

## 日志框架切换(仅在需要时执行)

若项目当前使用 logback 且需切换到 log4j2 才能使用 RewriteAppender:

**Step 1: 排除 logback**

在启动模块 pom.xml 中:

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </exclusion>
    </exclusions>
</dependency>
```

**Step 2: 添加 log4j2 starter**

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
```

**Step 3: 删除或重命名 logback-spring.xml**

避免两个日志框架冲突。

---

## 验证清单

完成全部配置后,逐项验证:

```
Task Progress:
- [ ] log4j-core 依赖已存在且版本 ≥ 2.17.1
- [ ] log4j2 配置文件中每个 Appender 都有对应的 Rewrite 包装
- [ ] 所有 appender-ref 引用名称已同步更新为 Rewrite 后的名称
- [ ] 启动类已添加 @EnableLogDesensitize 注解
- [ ] se.properties 已生成且正则语法正确
- [ ] mvn clean compile 编译通过
- [ ] 启动应用后日志中敏感数据已被 masking
```

---

## 异常处理

| 场景 | 处理方式 |
|------|---------|
| log4j-core 版本低于 2.17.1 | 必须升级,存在 Log4Shell 安全漏洞 |
| 项目同时存在 logback 和 log4j2 | 确认实际生效的日志框架,避免配置冲突 |
| RewriteAppender 不生效 | 检查 log4j2 配置文件是否被正确加载(文件名、classpath 位置) |
| se.properties 正则语法错误 | 启动时会报 PatternSyntaxException,按提示修正正则 |
| 切换日志框架后启动失败 | 检查是否有其他组件依赖 logback 特有 API |

---

## 常见陷阱提醒

1. **logback 与 log4j2 共存冲突**:Spring Boot 默认使用 logback,切换到 log4j2 必须排除 `spring-boot-starter-logging`,否则会出日志重复输出或启动异常
2. **RewriteAppender 名称必须匹配**:如果 `<root>` 中引用的是 `STDOUT`,但 Rewrite 后的 Appender 名称不是 `STDOUT`,日志将丢失且无报错
3. **se.properties 路径问题**:`${sys:user.dir}` 指向 JVM 启动目录,可能不是项目根目录。建议用 `${sys:user.dir}/se.properties` 或 classpath 方式加载
4. **正则性能**:复杂正则在高频日志场景下可能影响性能,建议用 `JMH` 或简单压测验证
5. **HTML 格式日志**:RewriteAppender 对 HTMLLayout 输出的脱敏效果有限,HTML 标签可能干扰正则匹配
6. **版本管理**:log4j-core 版本应跟随 Spring Boot BOM,不要硬编码版本号

0

评论区