logback日志脱敏后异步写入文件

news/2024/12/27 23:30:17/

        大家项目中肯定都会用到日志打印,目的是为了以后线上排查问题方便,但是有些企业对输出的日志包含的敏感(比如:用户身份证号,银行卡号,手机号等)信息要进行脱敏处理。

       哎!我们最近就遇到了日志脱敏的改造。可以说是一波三折。

        浩浩荡荡的日志脱敏改造就这样开始..........

              第一季 :在项目中所有log.info方法输出日志的地方手工脱敏,很low的一种方法

        初期想法很简单,就是根据业务,判断哪些info输出的地方可能包含敏感信息,然后进行手工脱敏。但是有很大弊端,一是工作量大,二是业务不熟悉很难一次改全。

脱敏工具类:

package com.lsl.utills;public class Utils {public static String checkParams(String str){if (str != null){String strnew = str.replaceAll("1[3-9]\\d{9}","***");return strnew;}else {return "";}}
}

代码中调用这个工具类:

package com.lsl.controller;import com.lsl.utills.Utils;
import com.lsl.vo.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
@RequestMapping("/lsl")
public class LogbackTestController {private static final Logger LOG = LoggerFactory.getLogger(LogbackTestController.class);@PostMapping(value = "qryUser", produces = "application/json;charset=UTF-8")@ResponseBodypublic String qryUser(){User user = new User();user.setUserName("LSL");user.setUserPwd("123@asd");user.setUserAddr("北京海淀");user.setUserAge(21);user.setUserTel("15803125588");LOG.info("用户信息:{}",user.toString());LOG.info("脱敏后的用户信息:{}", Utils.checkParams(user.toString()));return "success";}
}

结果截图:

        这种情况确实也能达到脱敏的目的,但是如果代码中有好多info日志输出,那么都需要改动,工作量太大,还容易拉下没有改动的地方。其实我们初期就是这种脱敏,反复改了好几次,总有遗漏的地方忘记改了。

        第二季:想研究一种省时省力的方式,能不能通过AOP切入log.info方法进行统一脱敏

大概思路,就是创建一个切面,在调用info方法前把打印内容通过上面的工具类脱敏后在打印。

切面:

package com.lsl.config;import com.lsl.utills.Utils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;@Aspect
public class LogInfoAspect {@Pointcut("execution(* org.slf4j.Logger.info(..))")public void loginfoPointcut(){}@Before("loginfoPointcut() && args(msg)")public void bfLogInfo(ProceedingJoinPoint joinPoint,String msg) throws Throwable{System.out.println("切入前参数msg=" + msg);String newStr = Utils.checkParams(msg);System.out.println("脱敏处理后参数=" + newStr);joinPoint.proceed(new Object[]{newStr});}@Around("loginfoPointcut() && args(msg)")public void adLogInfo(ProceedingJoinPoint joinPoint,String msg) throws Throwable{
//        String arg = (String)joinPoint.getArgs()[0];System.out.println("切入前参数msg=" + msg);String newStr = Utils.checkParams(msg);System.out.println("脱敏处理后参数=" + newStr);joinPoint.proceed(new Object[]{newStr});}
}

结果是:没法切入到info方法,也就无法脱敏了

如下图:

原因也很简单:AOP是交付spring托管的bean才能使用动态代理,实现切入进行操作。然后logback并没有由spring托管,所以不行。

我后来想到,在配置类里交付spring托管不就行了,但是也不行,大家猜猜是什么原因呢?

                        第三季:利用logback脱敏并实现异步写入日志文件

大概思路是,需要自己写脱敏转换器,然后引入logback.xml中,异步写入文件比较方便,网上好多资料,大家可以自行查询。

脱敏转换器:

package com.lsl.config;import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.apache.commons.lang3.StringUtils;import java.util.regex.Matcher;
import java.util.regex.Pattern;public class SensitiveConverter extends MessageConverter {//手机号正则private static final String PHONE_REG = "(1([358][0-9]|4[579]|66|7[0135678]|9[89])[0-9]{8}|[96]\\d{7}|6[68]\\d{5}|09\\d{8})([^@^\\d]|$)";//身份证号正则private static final String IDCARD_REG = "";//脱敏字符private static final String SECRET = "***************";//要脱敏的类型private static String sensitiveType;public static void setSensitiveType(String sensitiveType){SensitiveConverter.sensitiveType = sensitiveType;}@Overridepublic String convert(ILoggingEvent event){String requestLogMsg = event.getFormattedMessage();return filtrSensitive(requestLogMsg,sensitiveType);}public String filtrSensitive(String content,String sensitiveType){try {if (StringUtils.isBlank(content)){return content;}//手机号脱敏if (sensitiveType.contains("phone")){content = filterPhone(content);}//身份证脱敏if (sensitiveType.contains("idcard")){}return content;} catch (Exception e) {return content;}}private static String filterPhone(String num){Pattern pattern =  Pattern.compile(PHONE_REG);Matcher matcher = pattern.matcher(num);StringBuffer sb = new StringBuffer();while (matcher.find()){String matchResult = matcher.group();if (!StringUtils.isNumeric(matchResult.substring(matchResult.length() - 1))){//大陆手机号if (matchResult.startsWith("1")){matcher.appendReplacement(sb,baseSensitive(matchResult,3,5));}//港澳手机号if (matchResult.startsWith("6") || matchResult.startsWith("9")){}}else {//大陆手机号if (matchResult.startsWith("1")){matcher.appendReplacement(sb,baseSensitive(matchResult,3,4));}//港澳手机号if (matchResult.startsWith("6") || matchResult.startsWith("9")){}}}matcher.appendTail(sb);return sb.toString();}private static String baseSensitive(String str,int startlen,int endlen){if (StringUtils.isBlank(str)){return "";}StringBuffer sb = new StringBuffer();sb.append(SECRET);return StringUtils.left(str,startlen).concat(StringUtils.leftPad(StringUtils.right(str,endlen),str.length()- startlen,sb.toString()));}}
package com.lsl.config;import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;@Component
public class SensitiveConverterProperties implements InitializingBean {@Value("${logging.sensitive.type}")private String sensitiveType;@Overridepublic void afterPropertiesSet() throws Exception {SensitiveConverter.setSensitiveType(this.sensitiveType);}public String getSensitiveType() {return sensitiveType;}public void setSensitiveType(String sensitiveType) {this.sensitiveType = sensitiveType;}
}

application.properties文件


server.port=8080logging.sensitive.type=phone

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false"><!--    转换配置 必选配置--><conversionRule conversionWord="sensitiveMsg" converterClass="com.lsl.config.SensitiveConverter"> </conversionRule><!-- 控制台输出 --><appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %sensitiveMsg%n</pattern></encoder></appender><!-- 按照每天生成日志文件 --><appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"><file>logs/app.log</file><rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"><fileNamePattern>logs/app.%d{yyyy-MM-dd}.log</fileNamePattern><maxHistory>30</maxHistory></rollingPolicy><encoder><pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %sensitiveMsg%n</pattern></encoder></appender><!-- 异步日志配置 --><appender name="ASYNC_FILE" class="ch.qos.logback.classic.AsyncAppender"><appender-ref ref="ROLLING" /></appender><!-- 项目包下所有info日志输出到控制台 --><logger name="com.lsl" level="INFO" additivity="false"><appender-ref ref="CONSOLE" /></logger><!-- 项目包下所有info日志异步输出到文件 --><logger name="com.lsl" level="INFO" additivity="false"><appender-ref ref="ASYNC_FILE" /></logger><!-- Root Logger --><root level="INFO"><appender-ref ref="CONSOLE" /><appender-ref ref="ROLLING" /></root>
</configuration>

controoler验证

package com.lsl.controller;import com.lsl.vo.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;@Controller
@RequestMapping("/lsl")
public class LogbackTestController {private static final Logger LOG = LoggerFactory.getLogger(LogbackTestController.class);@PostMapping(value = "qryUser", produces = "application/json;charset=UTF-8")@ResponseBodypublic String qryUser(){User user = new User();user.setUserName("LSL");user.setUserPwd("123@asd");user.setUserAddr("北京海淀");user.setUserAge(21);user.setUserTel("15803125588");LOG.info("用户信息:{}",user.toString());return "success";}
}

验证截图

总结:

利用logback的脱敏转换器是非常方便且灵活的。自己可以定义各种业务敏感信息的脱敏策略,我这里面只写了手机号的脱敏策略。而且在配置文件properties中可以配置对那些敏感信息进行脱敏。

比如:logging.sensitive.type=phone,idcard  

这样就是手机号和身份证号同时进行脱敏,只是具体的身份证号的脱敏规则,我以后在补上吧。


http://www.ppmy.cn/news/1543302.html

相关文章

推荐一款优秀的pdf编辑器:Ashampoo PDF Pro

Ashampoo PDF Pro是管理和编辑 PDF 文档的完整解决方案。程序拥有您创建、转换、编辑和保护文档所需的一切功能。根据需要可以创建特定大小的文档&#xff0c;跨设备可读&#xff0c;还可以保护文件。现在您还能像编辑Word文档一样编辑PDF! 软件特点 轻松处理文字 如 Microso…

深入理解Docker,从入门到精通-Part1(基础使用)

一、Docker基本概念 Docker架构 基本组件的介绍 Docker Client 是用户界面&#xff0c;它支持用户与Docker Daemon之间通信 Docker Daemon Docker最核心的后台进程&#xff0c;运行于主机上&#xff0c;处理服务请求 Docker registry是中央registry&#xff0c;支持拥有公有与…

《原子核物理评论》

《原子核物理评论》报道原子核物理基础研究、应用研究以及与此有关的交叉学科研究领域的最新重要研究成果和发展趋势, 促进本学科及相关交叉学科的发展&#xff0c;增进国内外学术交流。主要刊登反映本学科及相关各交叉学科领域最新进展的评述性文章, 同时及时发表最新重要研究…

Elasticsearch 安装教程:驾驭数据海洋的星际导航仪

目录 一、准备工作1. ES的下载 二、安装步骤三、注意事项四、启动报错1. org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root2. max virtual memory areas vm.max_map_count [65530] is too low, increase to at l…

基于数字图像处理人民币面额自动识别

自动缴费机、存取款一体机和自动售货机出现在了我们的生活中。这些先进的设备中最重要的一门技术就是&#xff1a;纸币识别技术。当我们将钞票投入缴费机或者存款机时&#xff0c;机器必定会首先识别用户投入的是面额为多少的纸币&#xff0c;之后再进行真伪辨别、数据写入等功…

【Linux】ProxySQL读写分离

proxysql-2.7.1-1-centos7.x86_64.rpm 读写分离 读写分离的概念 读写分离是⼀种数据库优化技术&#xff0c;主要⽬的是通过将数据库的读操作和写操作分散到不同的数据库 实例上&#xff0c;来提⾼数据库的整体性能和可扩展性。其基本原理是让主数据库处理事务性增、改、删操…

Github 2024-10-30C开源项目日报 Top10

根据Github Trendings的统计,今日(2024-10-30统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量C项目10C++项目1PHP项目1PHP:流行的Web开发脚本语言 创建周期:4710 天开发语言:C, PHP协议类型:OtherStar数量:37340 个Fork数量:7657 次…

微信小程序-全局数据共享/页面间通信

一.全局数据共享 声明全局的变量&#xff0c;在app.js文件里 App({//全局共享的数据globalData:{token:},//设置全局数据setToken(token){this.globalData.tokentoken}})使用 getApp() 获取全局App实例 //返回全局唯一的APP实例 const appInstancegetApp()Page({login(){con…