添加依赖

注意:springboot版本2.7.0

        <dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>

自定义枚举值

/**
* 脱敏枚举值
*/
public enum SensitiveType {
DEFAULT, // 默认规则(部分隐藏)
PHONE, // 手机号
ID_CARD, // 身份证号
EMAIL, // 邮箱
ADDRESS, //地址
USER_NAME, //用户名
NUMBER; //数字,直接全脱敏
}

自定义注解

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Sensitive {
SensitiveType type() default SensitiveType.DEFAULT;
}

重写ClassicConverter

import ch.qos.logback.classic.pattern.ClassicConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.ybchen.log.Sensitive;
import com.ybchen.log.SensitiveType;
import org.slf4j.helpers.MessageFormatter; import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List; /**
* @description: 敏感日志转换器
* @author: 陈彦斌
* @create: 2024-12-09 18:55
*/
public class SensitiveLogConverter extends ClassicConverter { @Override
public String convert(ILoggingEvent event) {
Object[] args = event.getArgumentArray();
if (args == null || args.length == 0) {
return event.getFormattedMessage();
}
for (int i = 0; i < args.length; i++) {
Object arg = args[i];
if (arg != null) {
args[i] = desensitize(arg);
}
}
return MessageFormatter.arrayFormat(event.getMessage(), args).getMessage();
} private Object desensitize(Object obj) {
if (obj == null) {
return null;
}
try {
Class<?> clazz = obj.getClass();
// 如果对象是集合类型
if (obj instanceof List) {
List<?> list = (List<?>) obj;
List<Object> newList = new ArrayList<>();
for (Object item : list) {
newList.add(desensitize(item)); // 递归处理列表中的每个对象
}
return newList;
}
// 如果对象是普通类型,直接返回
if (isPrimitiveOrWrapper(clazz) || clazz == String.class) {
return obj;
}
// 创建新实例
Object newObj = clazz.getDeclaredConstructor().newInstance();
// 遍历字段
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object fieldValue = field.get(obj); // 获取原字段值
if (fieldValue == null){
continue;
}
if (field.isAnnotationPresent(Sensitive.class)) {
// 处理标注为 @Sensitive 的字段
if (fieldValue instanceof String) {
Sensitive sensitive = field.getAnnotation(Sensitive.class);
SensitiveType type = sensitive.type();
field.set(newObj, applyDesensitization((String) fieldValue, type));
}else if (
fieldValue instanceof Integer
|| fieldValue instanceof Long
|| fieldValue instanceof BigDecimal
|| fieldValue instanceof Byte
|| fieldValue instanceof Short
|| fieldValue instanceof Float
|| fieldValue instanceof Double
){
//数字直接脱敏
field.set(newObj, null);
}else if (fieldValue instanceof List) {
// 如果字段是 List 类型,递归处理其内部元素
List<?> list = (List<?>) fieldValue;
List<Object> newList = new ArrayList<>();
for (Object item : list) {
newList.add(desensitize(item));
}
field.set(newObj, newList);
} else {
// 对其他非字符串类型的字段,递归脱敏
field.set(newObj, desensitize(fieldValue));
}
} else {
// 未标注 @Sensitive 的字段直接复制
field.set(newObj, fieldValue);
}
}
return newObj; // 返回脱敏后的新对象
} catch (Exception e) {
e.printStackTrace();
return obj; // 异常情况下返回原对象
}
} private String applyDesensitization(String value, SensitiveType type) {
if (value==null || "".equals(value)){
return "";
}
switch (type) {
case DEFAULT:
return value.replaceAll("^(.{3}).*$", "$1*****");
case PHONE:
if (value.length()<11||value.length()>11){
return value.replaceAll("^(.{5}).*$", "$1*****");
}
return value.replaceAll("(\\d{3})\\d{4}(\\d{4})", "$1****$2");
case ID_CARD:
if (value.length()<18){
return value.replaceAll("^(.{5}).*$", "$1*****");
}
return value.replaceAll("(\\d{4})\\d{10}(\\d{4})", "$1**********$2");
case EMAIL:
if (!value.contains("@")){
return value.replaceAll("^(.{5}).*$", "$1*****");
}
return value.replaceAll("(?<=^.{3}).*(?=@)", "*****");
case ADDRESS:
if (value.length() <= 9) {
return value.replaceAll("(.)(.*)(..)", "$1*$2$3");
}
return value.replaceAll("(.{3}).*(.{3})", "$1*****$2");
case USER_NAME:
return value.replaceAll("(\\S)\\S(\\S*)", "$1*$2");
default:
return value;
}
} private boolean isPrimitiveOrWrapper(Class<?> clazz) {
return clazz.isPrimitive() || clazz == Boolean.class || clazz == Integer.class ||
clazz == Long.class || clazz == Double.class || clazz == Float.class ||
clazz == Byte.class || clazz == Short.class || clazz == BigDecimal.class || clazz == Character.class;
}
}

logback-spring.xml配置

<configuration>
<conversionRule conversionWord="sensitive" converterClass="com.ybchen.config.SensitiveLogConverter" /> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %sensitive{%msg}%n</pattern>
</encoder>
</appender> <root level="info">
<appender-ref ref="CONSOLE" />
</root>
</configuration>

VO类

@Data
public class UserVO {
@Sensitive(type = SensitiveType.USER_NAME)
private String userName;
@Sensitive(type = SensitiveType.EMAIL)
private String email;
@Sensitive(type = SensitiveType.PHONE)
private String phone;
@Sensitive(type = SensitiveType.ID_CARD)
private String idCard;
@Sensitive(type = SensitiveType.ADDRESS)
private String address;
@Sensitive
private Integer age;
@Sensitive
private BigDecimal money;
@Sensitive
private String content;
}

打印日志

    @PostMapping("test")
public Object test(@RequestBody List<UserVO> userList){
UserVO vo=new UserVO();
vo.setUserName("张三");
vo.setAddress("广东省广州市天河区xxxxxx号");
vo.setAge(18);
log.info("日志脱敏----info:\r\n{}\r\n{}", userList,vo);
log.info("\r\n");
log.error("日志脱敏----error:\r\n{}\r\n{}", userList,vo);
System.out.println("正常获取脱敏数据 userName:"+vo.getUserName());
System.out.println("正常获取脱敏数据 address:"+vo.getAddress());
return userList;
}

效果

SpringBoot+logback 日志打印脱敏,正常获取对象不受影响的更多相关文章

  1. springboot+logback日志输出企业实践(下)

    目录 1.引言 2. 输出 logback 状态数据 3. logback 异步输出日志 3.1 异步输出配置 3.2 异步输出原理 4. springboot 多环境下 logback 配置 5. ...

  2. springboot+logback日志输出企业实践(上)

    目录 1.引言 2.logback简介 3. springboot默认日志框架-logback 3.1 springboot示例工程搭建 3.2 日志输出与基本配置 3.2.1 日志默认输出 3.2. ...

  3. 8. springboot logback 日志整合

    在resources目录下,新建log/logback-spring.xml文件,内容如下: <?xml version="1.0" encoding="UTF-8 ...

  4. springboot logback日志的使用

    以下有两个使用,一个是简单使用,另一个是需要进行详细的配置再使用.首先给出源代码.可以直接使用 import org.slf4j.Logger;import org.slf4j.LoggerFacto ...

  5. logback日志打印sql

    今天整合springboot2 + mybatis + logback 遇到了在日志中sql打印不出来的坑,在网上找了好久,都不是我遇到的问题,这里吐槽一下下现在的博客质量,好多都是抄袭的,也没有标注 ...

  6. SpringBoot统一日志打印

    统一日志打印 @Slf4j @Aspect @Component public class ControllerLog { private static final ThreadLocal<Lo ...

  7. SpringBoot Logback日志配置

    Logback的配置介绍: 1.Logger.appender及layout Logger作为日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型.级别. ...

  8. vscode springboot logback 日志输出到不同文件

    参照了:https://blog.csdn.net/appleyk/article/details/78717388# 在src\main\resources中新建一个logback-boot.xml ...

  9. SpringBoot Logback 日志配置

    目录 前言 日志格式 日志输出 日志轮替 日志级别 日志分组 小结 前言 之前使用 SpringBoot 的时候,总是习惯于将日志框架切换为 Log4j2,可能是觉得比较靠谱,也可能年龄大了比较排斥新 ...

  10. Spring-boot logback日志处理

    1:在resources目录下面创建logback.xml配置文件 <?xml version="1.0"?> <configuration> <!- ...

随机推荐

  1. MyBatisPlus——标准数据层开发

    标准数据层开发 标准数据层CRUD功能 lombok 一个java类库,提供了一组注解,简化POJO实体类开发    常用注解@Data    为当前实体类在编译期设置对应的get/set方法,无参/ ...

  2. 解决 SHADERed无法打开,报错 缺失XINPUT1_4.DLL 文件的办法

    起因: 在给某个不知名的同事安装软件时遇到的一个问题,安装完成后打开软件报错:SHADERed无法打开,报错 缺失XINPUT1_4.DLL ,C++依赖项均已打上,卸载C++插件后依然报错只能找缺失 ...

  3. hive操作 -- zeppelin安装及配置

    当时写hive用的是zeppelin,这个工具可以直接在页面上写sql语句,操作服务器上的hive库,还挺方便的 通过zeppelin实现hive的查询结果的可视化 启动过程中会报错:User: to ...

  4. USB ncm虚拟网卡

    NCM介绍 1 功能 USB NCM,属于USB-IF定义的CDC(Communication Device Class)下的一个子类:Network Control Model,用于Host和Dev ...

  5. Android usb广播 ACTION_USB_DEVICE_ATTACHED流程源码分析

    整体流程图 大概意思就是UsbHostManager启动监控线程,monitorUsbHostBus会调用usb_host_run函数(使用inotify来监听USB设备的插拔)不停的读取bus总线, ...

  6. 2024年7月中国数据库排行榜:PolarDB独领云风骚,达梦跨越新巅峰

    在7月发布的中国数据库流行度排行榜中,各大国产数据库厂商在不同领域表现势如破竹,PolarDB以800分刷新记录,并在SIGMOD 2024上获得"最佳论文奖":OceanBase ...

  7. 嘟嘟牛在线登陆加密分析-RPC调用

    加密参数 JADX反编译后搜索代码 user/login 基本可以确定就是从这里发起网络请求 跟进 addRequestMap 方法分析 这一看逻辑就清晰了 先添加一个时间戳,在对sign进行加密后在 ...

  8. 不要慌,FastGPT 告诉我这是技术性调整,利好大 A!

    一觉醒来,股市又变天了,到处一片哀嚎,我看了下前几天牛市的赚钱名单,咱们公众号的粉丝没有一个在里面,说实话很失望,希望大家多做些有意义的事情,而不是整天虚度光阴.一个个平时看着都挺厉害,也没赚到钱,我 ...

  9. KubeKey 升级 Kubernetes 次要版本实战指南

    作者:运维有术 前言 知识点 定级:入门级 KubeKey 如何升级 Kubernetes 次要版本 Kubernetes 升级准备及验证 KubeKey 升级 Kubernetes 的常见问题 实战 ...

  10. centos7系统安装部署zabbix5.0

    一.简介 zabbix是一个基于[WEB]界面的提供分布式[系统监视]以及网络监视功能的企业级的开源解决方案.zabbix能监视各种网络参数,保证[服务器系统]的安全运营:并提供灵活的通知机制以让[系 ...