精华内容
下载资源
问答
  • 日志脱敏
    2022-06-25 15:13:25

    环境搭建

    搭建基础的spring boot工程,引入logback-spring.xml基础础配置文件

    基本实现方式

    修改配置

    在logback-spring文件的基础上添加脱敏开关,启动监听器,及日志转换器

    <!--是否脱敏配置-->
    <if condition='"false".equals(property("SENSITIV_FLAG"))'>
        <then>
          <!-- 关闭日志脱敏 -->
          <property scope="context" name="converterCanRun" value="false"/>
        </then>
       <else>
        <!-- 开启日志脱敏 -->
          <property scope="context" name="converterCanRun" value="true"/>
        </else>
    </if>
    <!--启动监听器-->
    <contextListener class="com.person.share.log.LoggerStartupListener"/>
    <!--日志转换器-脱敏-->
    <conversionRule conversionWord="msg" converterClass="com.person.share.log.SensitiveConverter"> </conversionRule>
    

    实现描述

    LoggerStartupListener的定义如下:
    用于初始化加载需要进行敏感字段处理的ContextListener,根据context对应的property加载自定义的需要的字段

    public class LoggerStartupListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
        
        private boolean started = false;
    
        public void start() {
            if (!this.started) {
                    this.prepareSensitiveDataKeyList();//加载自定义的脱敏字段,如添加一个手机号的字段列表
                    this.started = true;
            }
        }
        private void prepareSensitiveDataKeyList() {
            String allowRun = this.context.getProperty("converterCanRun");
            if ("true".equals(allowRun)) {
                List<String> phoneList = SensitiveConverter.phoneList;
                String extPhoneList = this.context.getProperty("SENSITIVE_PHONE");
                if (StringUtils.isNotBlank(extPhoneList)) {
                    phoneList.addAll(Arrays.asList(extPhoneList.split(",")));
                }
                SensitiveConverter.phoneList = phoneList;
                if (CollectionUtils.isNotEmpty(SensitiveConverter.sensitiveDataKeyList)) {
                    return;
                }
                SensitiveConverter.sensitiveDataKeyList.addAll(SensitiveConverter.ignoreList);
                SensitiveConverter.sensitiveDataKeyList.addAll(SensitiveConverter.phoneList);
            }
        }
        public boolean isStarted() {
            return this.started;
        }
    }
    

    SensitiveConverter的定义如下:
    脱敏规则的实现,如下是对通过phoneNo记录手机号的json字符串进行md5处理的一种可能方式

    public class SensitiveConverter extends MessageConverter {
    
        private String converterCanRun;
        public static List<String> ignoreList = Lists.newArrayList();
        /**
         * 包含电话号码的字段过滤
         */
        public static  List<String> phoneList = Arrays.asList("phoneNo","phone");
    
        public static List<String> sensitiveDataKeyList = Lists.newArrayList();
        @Override
        public String convert(ILoggingEvent event) {
            Context context = getContext();
            String allowRun = context.getProperty("converterCanRun");
            setConverterCanRun(StringUtils.defaultIfBlank(allowRun,"true"));//默认进行脱敏
            String originMsg =  event.getFormattedMessage();
            originMsg = sensitiveInvoke(originMsg);
            return originMsg;
        }
        private String sensitiveInvoke(String originMsg) {//originMsg的格式形如{"phoneNo":"12345678900"}
           String tempMsg = originMsg;
            if ("true".equals(this.converterCanRun)) {
                try {
                    if (sensitiveDataKeyList.size() > 0) {
                        Iterator var3 = sensitiveDataKeyList.iterator();
    
                        while(var3.hasNext()) {
                            String key = (String)var3.next();
                            int index = -1;
    
                            while(true) {
                                index = tempMsg.indexOf(key, index + 1);
                                if (index != -1 ) {
                                    int valueStart = this.getValueStartIndex(tempMsg, index + key.length());
                                    int valueEnd = this.getValueEndEIndex(tempMsg, valueStart);
                                    String subStr = tempMsg.substring(valueStart, valueEnd);
                                    subStr = mask(subStr, key);
                                    tempMsg = tempMsg.substring(0, valueStart) + subStr + tempMsg.substring(valueEnd);
                                }
                                if (index == -1) {
                                    break;
                                }
                            }
                        }
                    }
                } catch (Exception var9) {
                   
                }
            }
            return tempMsg;
        }
       private int getValueStartIndex(String msg, int valueStart) {
            while(valueStart < msg.length()) {
                char ch = msg.charAt(valueStart);
                if (ch == ':') {
                    ++valueStart;
                    ch = msg.charAt(valueStart);
                    if (ch == '"') {
                        ++valueStart;
                    }
                    return valueStart;
                }
                ++valueStart;
            }
            return valueStart;
        }
        private int getValueEndEIndex(String msg, int valueEnd) {
            while(true) {
                if (valueEnd != msg.length()) {
                    char ch = msg.charAt(valueEnd);
                    if (ch == '"') {
                        if (valueEnd + 1 != msg.length()) {
                            char nextCh = msg.charAt(valueEnd + 1);
                            if ( nextCh != ',' && nextCh !='}') {
                                ++valueEnd;
                                continue;
                            }
    
                            while(valueEnd > 0) {
                                char preCh = msg.charAt(valueEnd - 1);
                                if (preCh != '\\') {
                                    break;
                                }
                                --valueEnd;
                            }
                        }
                    } else if ( ch != ',' && ch != '}' ) {
                        ++valueEnd;
                        continue;
                    }
                }
                return valueEnd;
            }
        }
        private String mask(String subMsg, String key) {
            if (ignoreList.contains(key)) {
                return "*IGNORE*";
            } else if (phoneList.contains(key)) {
                return "'" + MD5Util.MD5(subMsg) + "'";
            }else {
                return subMsg;
            }
        }
        public void setConverterCanRun(String allowRun){
            this.converterCanRun = allowRun;
        }
    }
    

    MD5Util是md5的一个工具类实现

    实现效果

    如上启动对应的应用后,当使用日志log.info or其他方式记录日志时,原文为{“phoneNo”:“12345678900”}在日志文件中可能被记录为*{“phoneNo”:“‘dhfidhgidae3hgd8dgd’”}*这样的md5的概要描述。如果需要在控制台上也要以密文输出的话,可以脱敏配置之后添加ConsoleAppender,类似下面的配置

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>
                    %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger -  %X{url}?%X{queryString} code:%X{businessResult} - %msg%n
                </pattern>
                <charset>UTF-8</charset>
            </encoder>
    </appender>
    
    更多相关内容
  • 几乎是网上 能找到的 日志脱敏的所有实现 1、基于正则表达式的 日志脱敏实现 ,扩展logback 、log4j 2、springmvc 返回报文脱敏。 3、基于注解方式的脱敏。 大家选择使用。
  • 对于日志脱敏的方式有很多,常见的有①使用conversionRule标签,继承MessageConverter②书写一个脱敏工具类,在打印日志的时候对特定特字段进行脱敏返回。 两种方式各有优缺点:第一种方式需要修改代码,不符合开闭...
  • 主要介绍了简单了解Java日志脱敏框架sensitive,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • logback实现日志脱敏

    千次阅读 2022-05-27 17:16:55
    许多系统为了安全需要对敏感信息(如手机号、邮箱、姓名、身份证号、密码、卡号、住址等)的日志打印要求脱敏后才能输出,本文将结合个人经历及总结分享一种logback日志脱敏方式,log4j实现日志脱敏请移步 ...

    许多系统为了安全需要对敏感信息(如手机号、邮箱、姓名、身份证号、密码、卡号、住址等)的日志打印要求脱敏后才能输出,本文将结合个人经历及总结分享一种logback日志脱敏方式,log4j实现日志脱敏请移步

    代码地址

    https://gitee.com/BlueDriver/code-demo/tree/master/demo/log-sensitive

    准备工作

    定义脱敏转换相关的类

    定义RegexReplacement

    package my.logback;
    
    import java.util.regex.Pattern;
    
    public class RegexReplacement {
        /**
         * 脱敏匹配正则
         */
        private Pattern regex;
        /**
         * 替换正则
         */
        private String replacement;
        /**
         * Perform the replacement.
         *
         * @param msg The String to match against.
         * @return the replacement String.
         */
        public String format(final String msg) {
            return regex.matcher(msg).replaceAll(replacement);
        }
    
        public Pattern getRegex() {
            return regex;
        }
    
        public void setRegex(String regex) {
            this.regex = Pattern.compile(regex);
        }
    
        public String getReplacement() {
            return replacement;
        }
    
        public void setReplacement(String replacement) {
            this.replacement = replacement;
        }
    }
    

    定义MyLogbackReplaces

    package my.logback;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class MyLogbackReplaces {
        /**
         * 脱敏正则列表
         */
        private List<RegexReplacement> replace = new ArrayList<>();
        /**
         * 添加规则(因为replace类型是list,必须指定addReplace方法用以添加多个)
         *
         * @param replacement replacement
         */
        public void addReplace(RegexReplacement replacement) {
            replace.add(replacement);
        }
    
        public List<RegexReplacement> getReplace() {
            return replace;
        }
    
        public void setReplace(List<RegexReplacement> replace) {
            this.replace = replace;
        }
    }
    

    定义MyLogbackPatternLayout

    package my.logback;
    
    import ch.qos.logback.classic.PatternLayout;
    import ch.qos.logback.classic.spi.ILoggingEvent;
    import org.apache.logging.log4j.Logger;
    import org.apache.logging.log4j.status.StatusLogger;
    
    public class MyLogbackPatternLayout extends PatternLayout {
        /**
         * logger
         */
        private static final Logger LOGGER = StatusLogger.getLogger();
        /**
         * 正则替换规则
         */
        private MyLogbackReplaces replaces;
        /**
         * 是否开启脱敏,默认关闭(false)
         */
        private Boolean sensitive;
    
        public MyLogbackPatternLayout(MyLogbackReplaces replaces, Boolean sensitive) {
            super();
            this.replaces = replaces;
            this.sensitive = sensitive;
        }
    
        /**
         * 格式化日志信息
         *
         * @param event ILoggingEvent
         * @return 日志信息
         */
        @Override
        public String doLayout(ILoggingEvent event) {
            // 占位符填充
            String msg = super.doLayout(event);
            // 脱敏处理
            return this.buildSensitiveMsg(msg);
        }
    
        /**
         * 根据配置对日志进行脱敏
         *
         * @param msg 消息
         * @return 脱敏后的日志信息
         */
        public String buildSensitiveMsg(String msg) {
            if (sensitive == null || !sensitive) {
                // 未开启脱敏
                return msg;
            }
            if (this.replaces == null || this.replaces.getReplace() == null || this.replaces.getReplace().isEmpty()) {
                LOGGER.error("日志脱敏开启,但未配置脱敏规则,请检查配置后重试");
                return msg;
            }
    
            String sensitiveMsg = msg;
    
            for (RegexReplacement replace : this.replaces.getReplace()) {
                // 遍历脱敏正则 & 替换敏感数据
                sensitiveMsg = replace.format(sensitiveMsg);
            }
            return sensitiveMsg;
        }
    }
    

    定义MyLogbackPatternLayoutEncoder

    package my.logback;
    
    import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
    
    public class MyLogbackPatternLayoutEncoder extends PatternLayoutEncoder {
        /**
         * 正则替换规则
         */
        private MyLogbackReplaces replaces;
        /**
         * 是否开启脱敏,默认关闭(false)
         */
        private Boolean sensitive = false;
    
        /**
         * 使用自定义TbspLogbackPatternLayout格式化输出
         */
        @Override
        public void start() {
            MyLogbackPatternLayout patternLayout = new MyLogbackPatternLayout(replaces, sensitive);
            patternLayout.setContext(context);
            patternLayout.setPattern(this.getPattern());
            patternLayout.setOutputPatternAsHeader(outputPatternAsHeader);
            patternLayout.start();
            this.layout = patternLayout;
            started = true;
        }
    
        public boolean isSensitive() {
            return sensitive;
        }
    
        public void setSensitive(boolean sensitive) {
            this.sensitive = sensitive;
        }
    
        public MyLogbackReplaces getReplaces() {
            return replaces;
        }
    
        public void setReplaces(MyLogbackReplaces replaces) {
            this.replaces = replaces;
        }
    }
    

    配置文件

    上一步中我们已经定义好了脱敏相关的类了,接下来将其运用到我们的logback配置文件中去

    logback.xml文件配置

    脱敏规则与log4j实现日志脱敏一文中的原理大同小异,本文不再赘述,请移步查看

    <?xml version="1.0" encoding="UTF-8"?>
    <!--please pay attention that: file name should not be logback.xml,
    name it logback-spring.xml to use it in springboot framework-->
    <configuration>
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <!-- 指定为自己写的PatternLayoutEncoder -->
            <encoder class="my.logback.MyLogbackPatternLayoutEncoder">
                <pattern>%d{HH:mm:ss.SSS} %-5level %logger{80} --- %msg%n</pattern>
                <!-- 日志字符集(默认ISO-8859-1) -->
                <charset>UTF-8</charset>
                <!-- 开启脱敏(默认false) -->
                <sensitive>true</sensitive>
                <!-- 脱敏规则列表 -->
                <replaces>
                    <!-- 脱敏规则 -->
                    <replace>
                        <!-- 11位的手机号:保留前3后4 -->
                        <regex>
                            <![CDATA[
    				(mobile|手机号)(=|=\[|\":\"|:|:|='|':')(1)([3-9]{2})(\d{4})(\d{4})(\]|\"|'|)
    							]]>
                        </regex>
                        <replacement>$1$2$3$4****$6$7</replacement>
                    </replace>
                    <replace>
                        <!-- 固定电话: XXXX-XXXXXXXX或XXX-XXXXXXXX,保留区号+前2后2 -->
                        <regex>
                            <![CDATA[
    				(tel|座机)(=|=\[|\":\"|:|:|='|':')([\d]{3,4}-)(\d{2})(\d{4})(\d{2})(\]|\"|'|)
    							]]>
                        </regex>
                        <replacement>$1$2$3$4****$6$7</replacement>
                    </replace>
    
                    <replace>
                        <!-- 地址:汉字+字母+数字+下划线+中划线,留前3个汉字 -->
                        <regex>
                            <![CDATA[
    				(地址|住址|address)(=|=\[|\":\"|:|:|='|':')([\u4e00-\u9fa5]{3})(\w|[\u4e00-\u9fa5]|-)*(\]|\"|'|)
    							]]>
                        </regex>
                        <replacement>$1$2$3****$5</replacement>
                    </replace>
    
    
                    <replace>
                        <!-- 19位的卡号,保留后4 -->
                        <regex>
                            <![CDATA[
    				(cardNo|卡号)(=|=\[|\":\"|:|:|='|':')(\d{15})(\d{4})(\]|\"|'|)
    							]]>
                        </regex>
                        <replacement>$1$2***************$4$5</replacement>
                    </replace>
    
                    <replace>
                        <!-- 姓名,2-4汉字,留前1-->
                        <regex>
                            <![CDATA[
    				(name|姓名)(=|=\[|\":\"|:|:|='|':')([\u4e00-\u9fa5]{1})([\u4e00-\u9fa5]{1,3})(\]|\"|'|)
    							]]>
                        </regex>
                        <replacement>$1$2$3**$5</replacement>
                    </replace>
    
                    <replace>
                        <!--  密码 6位数字,全* -->
                        <regex>
                            <![CDATA[
    					(password|密码|验证码)(=|=\[|\":\"|:|:|='|':')(\d{6})(\]|\"|'|)
    							]]>
                        </regex>
                        <replacement>$1$2******$4</replacement>
                    </replace>
    
                    <replace>
                        <!-- 身份证,18位(结尾为数字或X、x),保留前1后1 -->
                        <regex>
                            <![CDATA[
    							(身份证号|idCard)(=|=\[|\":\"|:|:|='|':')(\d{1})(\d{16})([\d|X|x]{1})(\]|\"|)
    							]]>
                        </regex>
                        <replacement>$1$2$3****************$5$6</replacement>
                    </replace>
    
                    <replace>
                        <!-- 邮箱,保留@前的前1后1 -->
                        <regex>
                            <![CDATA[
    							(\w{1})(\w*)(\w{1})@(\w+).com
    							]]>
                        </regex>
                        <replacement>$1****$3@$4.com</replacement>
                    </replace>
                </replaces>
    
            </encoder>
        </appender>
    
        <root level="info">
            <appender-ref ref="STDOUT"/>
        </root>
    
    </configuration>
    

    测试

    class Job {
        /**
         * jobName
         */
        private String jobName;
        /**
         * salary
         */
        private int salary;
        /**
         * company
         */
        private String company;
        /**
         * address
         */
        private String address;
        /**
         * tel
         */
        private String tel;
        /**
         * position
         */
        private List<String> position;
    
    	// getter, setter, toString等省略
    }
    
    class User {
        /**
         * name
         */
        private String name;
        /**
         * idCard
         */
        private String idCard;
        /**
         * cardNo
         */
        private String cardNo;
        /**
         * mobile
         */
        private String mobile;
        /**
         * tel
         */
        private String tel;
        /**
         * password
         */
        private String password;
        /**
         * email
         */
        private String email;
        /**
         * address
         */
        private String address;
        /**
         * birth
         */
        private Date birth;
        /**
         * job
         */
        private Job job;
    	// getter, setter, toString等省略
    }
    
    public class LogSensitiveTest {
        private static final Logger logger = LoggerFactory.getLogger(LogSensitiveTest.class);
    
        @Test
        public void test0() {
            // 等号
            logger.infoMessage("mobile={}", "13511114444");
            // 等号+[
            logger.infoMessage("mobile=[{}]", "13511114444");
            // 英文单引号+等号
            logger.infoMessage("mobile='{}'", "13511114444");
            // 中文冒号
            logger.infoMessage("mobile:{}", "13511114444");
            // 英文冒号
            logger.infoMessage("mobile:{}", "13511114444");
            // 英文双引号+英文冒号
            logger.infoMessage("\"mobile\":\"{}\"", "13511114444");
            // 英文单引号+英文冒号
            logger.infoMessage("'mobile':'{}'", "13511114444");
        }
    
        /**
         * 基本输出
         */
        @Test
        public void test1() {
            // 11位手机号
            logger.infoMessage("mobile={}", "13511114444");
            logger.infoMessage("mobile={},手机号:{}", "13511112222", "13511113333");
            logger.infoMessage("手机号:{}", "13511115555");
            // 固定电话(带区号-)
            logger.infoMessage("tel:{},座机={}", "0791-83376222", "021-88331234");
            logger.infoMessage("tel:{}", "0791-83376222");
            logger.infoMessage("座机={}", "021-88331234");
    
            // 地址
            logger.infoMessage("address:{}", "浙江省杭州市滨江区光明大道8888号");
            logger.infoMessage("地址:{}", "上海市浦东区北京东路1-10号");
    
            // 19位卡号
            logger.infoMessage("cardNo:{}", "6227002020000101222");
    
            // 姓名
            logger.infoMessage("name={}, 姓名=[{}],name={},姓名:{}", "张三", "上官婉儿", "李云龙", "楚云飞");
    
            // 密码
            logger.infoMessage("password:{},密码={}", "123456", "456789");
    
            logger.infoMessage("password:{}", "123456");
            logger.infoMessage("密码={}", "123456");
    
            // 身份证号码
            logger.infoMessage("idCard:{},身份证号={}", "360123202111111122", "360123202111111122");
            logger.infoMessage("身份证号={}", "360123202111111122");
    
            // 邮箱
            logger.infoMessage("邮箱:{}", "zhangs12345@google.com");
            logger.infoMessage("email={}", "zhangs12345@google.com");
        }
    
        /**
         * toString/json输出
         */
        @Test
        public void test2() {
            User user = new User();
            user.setCardNo("6227002020000101222");
            user.setTel("0571-11112222");
            user.setBirth(new Date());
    
            user.setAddress("浙江省西湖区西湖路288号钱江乐园2-101室");
            user.setEmail("zhangs12345@google.com");
            user.setPassword("123456");
            user.setMobile("15911116789");
            user.setName("张三");
            user.setIdCard("360123202111111122");
    
            Job job = new Job();
            job.setAddress("浙江省杭州市滨江区某公司");
            job.setTel("0571-12345678");
            job.setJobName("操作员");
            job.setSalary(2000);
            job.setCompany("某某有限公司");
            job.setPosition(Arrays.asList("需求", "开发", "测试", "上线"));
    
            user.setJob(job);
    
            logger.infoMessage("用户信息:{}", user);
            logger.infoMessage("用户信息:{}", JSONUtil.toJsonStr(user));
        }
    }
    

    test0输出:

    18:19:38.012 INFO  log.test.LogSensitiveTest --- mobile=135****4444
    18:19:38.016 INFO  log.test.LogSensitiveTest --- mobile=[135****4444]
    18:19:38.017 INFO  log.test.LogSensitiveTest --- mobile='135****4444'
    18:19:38.018 INFO  log.test.LogSensitiveTest --- mobile:135****4444
    18:19:38.018 INFO  log.test.LogSensitiveTest --- mobile:135****4444
    18:19:38.018 INFO  log.test.LogSensitiveTest --- "mobile":"135****4444"
    18:19:38.019 INFO  log.test.LogSensitiveTest --- 'mobile':'135****4444'
    

    test1输出:

    18:23:23.115 INFO  log.test.LogSensitiveTest --- mobile=135****4444
    18:23:23.115 INFO  log.test.LogSensitiveTest --- mobile=135****2222,手机号:135****3333
    18:23:23.115 INFO  log.test.LogSensitiveTest --- 手机号:135****5555
    18:23:23.115 INFO  log.test.LogSensitiveTest --- tel:0791-83****22,座机=021-88****34
    18:23:23.115 INFO  log.test.LogSensitiveTest --- tel:0791-83****22
    18:23:23.115 INFO  log.test.LogSensitiveTest --- 座机=021-88****34
    18:23:23.115 INFO  log.test.LogSensitiveTest --- address:浙江省****
    18:23:23.115 INFO  log.test.LogSensitiveTest --- 地址:上海市****
    18:23:23.115 INFO  log.test.LogSensitiveTest --- cardNo:***************1222
    18:23:23.115 INFO  log.test.LogSensitiveTest --- name=张**, 姓名=[上**],name=李**,姓名:楚**
    18:23:23.115 INFO  log.test.LogSensitiveTest --- password:******,密码=******
    18:23:23.115 INFO  log.test.LogSensitiveTest --- password:******
    18:23:23.115 INFO  log.test.LogSensitiveTest --- 密码=******
    18:23:23.115 INFO  log.test.LogSensitiveTest --- idCard:3****************2,身份证号=3****************2
    18:23:23.115 INFO  log.test.LogSensitiveTest --- 身份证号=3****************2
    18:23:23.115 INFO  log.test.LogSensitiveTest --- 邮箱:z****5@google.com
    18:23:23.115 INFO  log.test.LogSensitiveTest --- email=z****5@google.com
    

    test2输出:

    18:24:55.460 INFO  log.test.LogSensitiveTest --- 用户信息:User{name='张**', idCard='3****************2', cardNo='***************1222', mobile='159****6789', tel='0571-11****22', password='******', email='z****5@google.com', address='浙江省****', birth=Fri May 27 18:24:55 GMT+08:00 2022, job=Job{jobName='操作员', salary=2000, company='某某有限公司', address='浙江省****', tel='0571-12****78', position=[需求, 开发, 测试, 上线]}}
    18:24:55.533 INFO  log.test.LogSensitiveTest --- 用户信息:{"address":"浙江省****","idCard":"3****************2","mobile":"159****6789","birth":1653647095451,"cardNo":"***************1222","password":"******","name":"张**","tel":"0571-11****22","job":{"jobName":"操作员","address":"浙江省****","salary":2000,"company":"某某有限公司","tel":"0571-12****78","position":["需求","开发","测试","上线"]},"email":"z****5@google.com"}
    

    实现原理

    本文实现日志脱敏,是借鉴了logback中自带的PatternLayoutEncoder类,重写了其start方法,在此方法中使用了我们自己的MyLogbackPatternLayout类创建格式化输出对象,MyLogbackPatternLayout类的doLayout方法中实现了正则替换的处理逻辑,可结合代码加断点测试以便更好了解具体过程

    代码地址

    https://gitee.com/BlueDriver/code-demo/tree/master/demo/log-sensitive

    相关推荐

    log4j实现日志脱敏的一种方式

    其他

    本文仅供参考,还望批评指正!

    展开全文
  • 对于日志脱敏的方式有很多,常见的有①使用conversionRule标签,继承MessageConverter②书写一个脱敏工具类,在打印日志的时候对特定特字段进行脱敏返回。两种方式各有优缺点:第一种方式需要修改代码,不符合开闭...

    在我们书写代码的时候,会书写许多日志代码,但是有些敏感数据是需要进行安全脱敏处理的。

    对于日志脱敏的方式有很多,常见的有①使用conversionRule标签,继承MessageConverter②书写一个脱敏工具类,在打印日志的时候对特定特字段进行脱敏返回。

    两种方式各有优缺点:

    • 第一种方式需要修改代码,不符合开闭原则。

    • 第二种方式,需要在日志方法的参数进行脱敏,对原生日志有入侵行为。

    自定义脱敏组件(slf4j+logback)

    一个项目在书写了很多打印日志的代码,但是后面有了脱敏需求,如果我们去手动改动代码,会花费大量时间。如果引入本组件,完成配置即可轻松完成脱敏。(仅需三步可轻松配置)

    一、自定义脱敏组件 - 脱敏效果演示

    ebc5f85ffce6d8523477d8a0f85baf5c.png


    a7f42a16b253310e4d62424777568173.png


    二、自定义脱敏组件 - 使用方式

    1、引入Jar包依赖

    前提是你将Jar包打入本地仓库,Jar包地址见后文。

    <dependency>
        <groupId>pers.liuchengyin</groupId>
        <artifactId>logback-desensitization</artifactId>
        <version>1.0.0</version>
    </dependency>

    2、替换日志文件配置类(logback.xml)

    日志打印方式都只需要替换成脱敏的类即可,如果你的业务不需要,则无需替换。

    ①ConsoleAppender - 控制台脱敏
    // 原类
    ch.qos.logback.core.ConsoleAppender
    // 替换类
    pers.liuchengyin.logbackadvice.LcyConsoleAppender
    ②RollingFileAppender - 滚动文件
    // 原类
    ch.qos.logback.core.rolling.RollingFileAppender
    // 替换类
    pers.liuchengyin.logbackadvice.LcyRollingFileAppender
    ③FileAppender - 文件
    // 原类
    ch.qos.logback.core.FileAppender
    // 替换类
    pers.liuchengyin.logbackadvice.LcyFileAppender

    替换示例:

    <property name="CONSOLE_LOG_PATTERN"
              value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) |%highlight(%-5level) |%blue(%thread) |%blue(%file:%line) |%green(%logger) |%cyan(%msg%n)"/>
    
    <!-- ConsoleAppender 控制台输出日志 -->
    <appender name="CONSOLE" class="pers.liuchengyin.logbackadvice.LcyConsoleAppender">
        <encoder>
            <pattern>
                ${CONSOLE_LOG_PATTERN}
            </pattern>
        </encoder>
    </appender>

    3、添加脱敏配置文件(logback-desensitize.yml)

    该配置文件应该放在resources文件下

    6ec02dbd6136126c429855daa26b5f3a.png


    三、自定义脱敏组件 - 脱敏规范

    1、支持数据类型

    八大基本类型及其包装类型、Map、List、业务里的Pojo对象、List<业务里的Pojo对象>、JSON字符串。

    注:在配置文件中配置的时候,只需要配置对象里的属性值就行。

    2、不支持的数据类型

    List<八大基本类型及包装类型>,因为不知道脱敏的数据源具体是哪一个。

    3、匹配规则

    key + 分割符 + value,目前仅支持冒号(:)和等号(=),示例如下:

    log.info("your email:{}, your phone:{}", "123456789@qq.com","15310763497");
    log.info("your email={}, your cellphone={}", "123456789@qq.com","15310763497");
    • key:定义了对应需要脱敏的关键字,如上诉的email、phone等以及业务对象中的字段、Map中的Key、JSON中的Key

    • value:需要脱敏的值,如上诉的123456789@qq.com15310763497

    4、日志规范

    建议书写日志的时候尽量规范,对于key为中文的是没有办法脱敏的,规范程度可以见脱敏效果演示里的代码。

    四、logback-desensitize.yml配置说明

    # 日志脱敏
    log-desensitize:
      # 是否忽略大小写匹配,默认为true
      ignore: true
      # 是否开启脱敏,默认为false
      open: true
      # pattern下的key/value为固定脱敏规则
      pattern:
        # 邮箱 - @前第4-7位脱敏
        email: "@>(4,7)"
        # qq邮箱 - @后1-3位脱敏
        qqemail: "@<(1,3)"
        # 姓名 - 姓脱敏,如*杰伦
        name: 1,1
        # 密码 - 所有需要完全脱敏的都可以使用内置的password
        password: password
      patterns:
        # 身份证号,key后面的字段都可以匹配以下规则(用逗号分隔)
        - key: identity,idcard
          # 定义规则的标识
          custom:
            # defaultRegex表示使用组件内置的规则:identity表示身份证号 - 内置的18/15位
            - defaultRegex: identity
              position: 9,13
            # 内置的other表示如果其他规则都无法匹配到,则按该规则处理
            - defaultRegex: other
              position: 9,10
        # 电话号码,key后面的字段都可以匹配以下规则(用逗号分隔)
        - key: phone,cellphone,mobile
          custom:
            # 手机号 - 内置的11位手机匹配规则
            - defaultRegex: phone
              position: 4,7
            # 自定义正则匹配表达式:座机号(带区号,号码七位|八位)
            - customRegex: "^0[0-9]{2,3}-[0-9]{7,8}"
            # -后面的1-4位脱敏
              position: "-<(1,4)"
            # 自定义正则匹配表达式:座机号(不带区号)
            - customRegex: "^[0-9]{7,8}"
              position: 3,5
            # 内置的other表示如果其他规则都无法匹配到,则按该规则处理
            - defaultRegex: other
              position: 1,3
        # 这种方式不太推荐 - 一旦匹配不上,就不会脱敏
        - key: localMobile
          custom:
              customRegex: "^0[0-9]{2,3}-[0-9]{7,8}"
              position: 1,3

    上面这个配置是相对完整的,一定要严格遵守层级配置格式。

    自定义脱敏支持的方式

    1、key:value的方式

    • phone:4,7,表示phone属性的4-7位进行脱敏

    • 原始数据:13610357861

    • 脱敏后:136****7861

    2、以符号作为起始、结束节点作为脱敏标志

    emai:"@>(4,7)"@为脱敏标志,>表示其为结束节点,<表示其为开始节点。即@>表示对@之前的进行脱敏,@<表示对@之后的进行脱敏。这个示例就是@前的数据的第4-7位进行脱敏。

    注意:这种规则里的双引号、括号不能省略,其次:=不能作为标志符号,因为和匹配规则有冲突

    • 原始数据:123456789@qq.com

    • "@>(4,7)"脱敏后:123****89@qq.com

    • "@<(1,3)"脱敏后:123456789@***com

    3、自定义正则脱敏

    patterns:
      # 手机号
      - key: phone,mobile
        custom:
          # 手机号的正则
          - customRegex: "^1[0-9]{10}"
            # 脱敏范围
            position: 4,7

    customRegex:正则表达式,如果符合该表达式,则使用其对应的脱敏规则(position)

    4、一个字段,根据多种值含义进行自定义脱敏

    比如说,username字段的值可以是手机号、也可以是邮箱,这个值动态改变的,前面几种方式都没办法解决,可以使用该方式。

    patterns:
      - key: username
        custom:
          # 手机号 - 11位
          - defaultRegex: phone
            position : 4,7
          # 邮箱 - @
       - defaultRegex: email
         position : "@>(3,12)"
       # 身份证 - 15/18位
       - defaultRegex: identity
         position : 1,3
       # 自定义正则
       - customRegex: "^1[0-9]{10}"
         position : 1,3
       # 都匹配不到时,按照这种规则来
       - defaultRegex: other
         position : 1,3

    注意:上面示例中匹配规则里的 双引号和括号 都不能省略

    该组件内置四种匹配规则:手机号、身份证号、邮箱、other(其他匹配不到时用的),内置一种脱敏方式:password,表示完全脱敏,可用于pattren下的。

    注:当pattern和patterns下的key有重复的时候,只会使用pattern下指定的方式进行脱敏。

    Jar包地址和源码地址

    https://github.com/liuchengyin01/LogbackDesensitization/tree/master/repo/pers/liuchengyin/logback-desensitization/1.0.0

    21d3641df2ed4593a50eaaddb51a0d48.png

    Github地址:

    https://github.com/liuchengyin01/LogbackDesensitization

    Jar包打入Maven本地仓库的方式

    1、下载Jar包,放在一个文件夹里

    2、在这个文件夹里打开cmd(打开cmd,进入到这个文件夹)

    3、执行命令(前提保证maven配置正常,使用mvn -v命令查看是否正常,如果显示版本号表示正常)

    mvn install:install-file -DgroupId=pers.liuchengyin -DartifactId=logback-desensitization -Dversion=1.0.0 -Dpackaging=jar -Dfile=logback-desensitization-1.0.0.jar

    命令说明:

    -DgroupId
     表示jar对应的groupId  
     <groupId>pers.liuchengyin</groupId>
    -DartifactId:
     表示jar对应的artifactId
     <artifactId>logback-desensitization</artifactId>
    -Dversion
     表示jar对应的 version
     <version>1.0.0</version>

    来源:https://blog.csdn.net/qq_40885085

    我们创建了一个高质量的技术交流群,与优秀的人在一起,自己也会优秀起来,赶紧点击加群,享受一起成长的快乐。另外,如果你最近想跳槽的话,年前我花了2周时间收集了一波大厂面经,节后准备跳槽的可以点击这里领取

    推荐阅读

    ··································

    你好,我是程序猿DD,10年开发老司机、阿里云MVP、腾讯云TVP、出过书创过业、国企4年互联网6年。从普通开发到架构师、再到合伙人。一路过来,给我最深的感受就是一定要不断学习并关注前沿。只要你能坚持下来,多思考、少抱怨、勤动手,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你看好一个事情,一定是坚持了才能看到希望,而不是看到希望才去坚持。相信我,只要坚持下来,你一定比现在更好!如果你还没什么方向,可以先关注我,这里会经常分享一些前沿资讯,帮你积累弯道超车的资本。

    点击领取2022最新10000T学习资料

    展开全文
  • 简易的第三方组件日志脱敏

    1.下载jar包并打入自己的本地仓库

    在这里插入图片描述
    随便找个地儿,mvn -v确保maven仓库配置正确
    在jar所在文件夹打开cmd终端,键入

    mvn install:install-file -DgroupId=pers.liuchengyin -DartifactId=logback-desensitization -Dversion=1.0.0 -Dpackaging=jar -Dfile=logback-desensitization-1.0.0.jar
    

    2.依赖

            <dependency>
                <groupId>pers.liuchengyin</groupId>
                <artifactId>logback-desensitization</artifactId>
                <version>1.0.0</version>
            </dependency>
    

    3.日志依赖

            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-logging</artifactId>
            </dependency>
    
    

    4.脱敏规则-logback-desensitize.yml

    # 日志脱敏
    log-desensitize:
      # 是否忽略大小写匹配,默认为true
      ignore: true
      # 是否开启脱敏,默认为false
      open: true
      # pattern下的key/value为固定脱敏规则
      pattern:
        # 邮箱 - @前第4-7位脱敏
        email: "@>(4,7)"
        # qq邮箱 - @后1-3位脱敏
        qqemail: "@<(1,3)"
        # 姓名 - 姓脱敏,如*杰伦
        name: 1,1
        # 密码 - 所有需要完全脱敏的都可以使用内置的password
        password: password
      patterns:
        # 身份证号,key后面的字段都可以匹配以下规则(用逗号分隔)
        - key: identity,idcard
          # 定义规则的标识
          custom:
            # defaultRegex表示使用组件内置的规则:identity表示身份证号 - 内置的18/15位
            - defaultRegex: identity
              position: 9,13
            # 内置的other表示如果其他规则都无法匹配到,则按该规则处理
            - defaultRegex: other
              position: 9,10
        # 电话号码,key后面的字段都可以匹配以下规则(用逗号分隔)
        - key: phone,cellphone,mobile
          custom:
            # 手机号 - 内置的11位手机匹配规则
            - defaultRegex: phone
              position: 4,7
            # 自定义正则匹配表达式:座机号(带区号,号码七位|八位)
            - customRegex: "^0[0-9]{2,3}-[0-9]{7,8}"
              # -后面的1-4位脱敏
              position: "-<(1,4)"
            # 自定义正则匹配表达式:座机号(不带区号)
            - customRegex: "^[0-9]{7,8}"
              position: 3,5
            # 内置的other表示如果其他规则都无法匹配到,则按该规则处理
            - defaultRegex: other
              position: 1,3
        # 这种方式不太推荐 - 一旦匹配不上,就不会脱敏
        - key: localMobile
          custom:
            customRegex: "^0[0-9]{2,3}-[0-9]{7,8}"
            position: 1,3
    

    5.logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration>
        <!--spring boot提供的logback的默认配置,必须引用,否则日志无法输出-->
        <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
    
        <!--配置集中管理的属性-->
        <property resource="application.yml"/>
    
        <!-- 彩色日志依赖的渲染类 -->
        <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
        <conversionRule conversionWord="wex"
                        converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
        <conversionRule conversionWord="wEx"
                        converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    
        <!--定义日志文件的存储地址 -->
        <property name="LOG_HOME" value="logs"/>
        <property name="FILE_NAME" value="${LOG_HOME}/%d{yyyy-MM-dd}"/>
        <!--定义自己个日志格式-->
        <property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level [%thread] %c{50} %M %L -- %m%n"/>
        <!--
            %d{yyyy-MM-dd HH:mm:ss.SSS} 日期格式
            %c 类的完整名称
            %logger 类的完整名称
            %M 方法名
            %L 行号
            %thread 线程
            %m 信息
            %n 换行
            %-5level 信息级别
        -->
    
        <!-- 控制台输出 -->
        <appender name="STDOUT" class="pers.liuchengyin.logbackadvice.LcyConsoleAppender">
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${PATTERN}</pattern>
            </encoder>
        </appender>
        <!-- 按照每天生成日志文件 -->
        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${FILE_NAME}_all.txt</FileNamePattern>
                <!--日志文件保留天数-->
                <MaxHistory>7</MaxHistory>
                <totalSizeCap>512MB</totalSizeCap>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${PATTERN}</pattern>
                <!--<pattern>%JsonOutPut %n</pattern>-->
            </encoder>
        </appender>
        <!-- 按照每天生成日志文件过滤消息级别 -->
        <appender name="FILE_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${FILE_NAME}_err.txt</FileNamePattern>
                <!--日志文件保留天数-->
                <MaxHistory>7</MaxHistory>
                <totalSizeCap>512MB</totalSizeCap>
            </rollingPolicy>
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${PATTERN}</pattern>
                <!--<pattern>%JsonOutPut %n</pattern>-->
            </encoder>
            <filter class="ch.qos.logback.classic.filter.LevelFilter">
                <!--设置过滤级别-->
                <level>ERROR</level>
                <!--过滤级别以上的处理方式-->
                <onMatch>ACCEPT</onMatch>
                <!--过滤级别以下的处理方式-->
                <onMismatch>DENY</onMismatch>
            </filter>
        </appender>
        <!-- 按照每天生成HTML日志文件 -->
        <appender name="FILE_HTML" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <!--日志文件输出的文件名-->
                <FileNamePattern>${FILE_NAME}_all.html</FileNamePattern>
                <!--日志文件保留天数-->
                <MaxHistory>7</MaxHistory>
                <totalSizeCap>512MB</totalSizeCap>
            </rollingPolicy>
            <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
                <layout class="ch.qos.logback.classic.html.HTMLLayout">
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS}%-5level%thread%c{50}%M%L%m</pattern>
                </layout>
            </encoder>
        </appender>
        <!--异步日志-->
        <appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
            <!--指定需要异步打印的日志-->
            <appender-ref ref="FILE" />
        </appender>
    
        <!-- 日志输出级别 -->
        <root level="INFO">
            <appender-ref ref="FILE_HTML"/>
            <appender-ref ref="FILE_ERROR"/>
            <appender-ref ref="FILE"/>
            <appender-ref ref="STDOUT"/>
        </root>
    </configuration>
    
    
    

    6.application.yml

    logging:
      config: classpath:logback.xml
    

    7.logback.xml种替换成脱敏的类

    ①ConsoleAppender - 控制台脱敏

    // 原类
    ch.qos.logback.core.ConsoleAppender
    // 替换类
    pers.liuchengyin.logbackadvice.LcyConsoleAppender
    

    ②RollingFileAppender - 滚动文件

    // 原类
    ch.qos.logback.core.FileAppender
    // 替换类
    pers.liuchengyin.logbackadvice.LcyFileAppender
    

    ③FileAppender - 文件

    // 原类
    ch.qos.logback.core.FileAppender
    // 替换类
    pers.liuchengyin.logbackadvice.LcyFileAppender
    

    替换示例:

      <!-- 控制台输出 -->
          <!-- 控制台输出 -->
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${PATTERN}</pattern>
            </encoder>
        </appender>
        替换
        <appender name="STDOUT" class="pers.liuchengyin.logbackadvice.LcyConsoleAppender">
            <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                <pattern>${PATTERN}</pattern>
            </encoder>
        </appender>
    

    8.测试

    @SpringBootApplication
    @Slf4j
    public class LogApplication {
        public static void main(String[] args) {
            SpringApplication.run(LogApplication.class , args);
            log.info("your email:{}, your phone:{}", "123456789@qq.com","15310763497");
            log.info("your name:{}", "周杰伦");
            log.info("your password:{}" , "15310763497");
            log.info("your identity:{}", "4408821777708052456");
            log.info("your identity:{}", "440882199910");
        }
    }
    

    9.demo结构

    在这里插入图片描述

    10.gitee地址

    https://gitee.com/xulehuang/example/tree/master/log-tuo-min

    11.参考

    12.资源以上传

    展开全文
  • 最详细demo--自定义日志脱敏组件,简单3 步完成 Spring Boot 的日志脱敏
  • 日志脱敏处理

    2021-09-16 10:58:20
    <?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" ...-- 日志脱敏配置开始,format:SensitiveRulesEnum--> <property scope="context" name="SensitiveDataKeys" value='[ ...
  • 于是,就写了一个基于 java 注解的日志脱敏工具。项目介绍日志脱敏是常见的安全需求。普通的基于工具类方法的方式,对代码的入侵性太强。编写起来又特别麻烦。本项目提供基于注解的方式,并且内置了常见的脱敏方式,...
  • 日志脱敏

    千次阅读 2018-03-20 10:06:51
    占位,稍后更新
  • JAVA 日志脱敏实现

    千次阅读 2022-04-21 09:58:11
    针对敏感数据进行脱敏输出,包括手机号、姓名、密码、身份证号、银行卡号和地址等,一般金融系统和电商系统在审计时要求会比较严格,需要进行脱敏输出。 脱敏方案的对比: 方案 优点 缺点 适用场景 ...
  • log4j实现日志脱敏

    千次阅读 2021-12-19 16:09:11
    许多系统为了安全需要对敏感信息(如手机号、邮箱、姓名、身份证号、密码、卡号、住址等)的日志打印要求脱敏后才能输出,本文将结合个人经历及总结分享一种log4j日志脱敏方式 自定义Layout import org.apache....
  • 对于日志脱敏的方式有很多,常见的有①使用conversionRule标签,继承MessageConverter②书写一个脱敏工具类,在打印日志的时候对特定特字段进行脱敏返回。 两种方式各有优缺点: 第一种方式需要修改代码,不符合开闭...
  • logback 日志脱敏
  • 对于日志脱敏的方式有很多,常见的有①使用conversionRule标签,继承MessageConverter②书写一个脱敏工具类,在打印日志的时候对特定特字段进行脱敏返回。 两种方式各有优缺点: 第一种方式需要修改代码,不符合开...
  • 对于日志脱敏的方式有很多,常见的有①使用conversionRule标签,继承MessageConverter②书写一个脱敏工具类,在打印日志的时候对特定特字段进行脱敏返回。两种方式各有优缺点:第一种方式需要修改代码,不符合开闭...
  • springboot log4j2 脱敏
  • Java实现日志脱敏处理

    万次阅读 热门讨论 2019-01-24 19:30:36
    Java 实现日志脱敏处理 题记 在日常工作中,日志处理是我们每一个程序员必备的素质,但是在有些场景下客户信息敏感,需要进行某些字段,或者某部分字段的脱敏处理。接到需求我们开始操刀! 需求分析 处理字段的...
  • 对敏感数据进行MD5加密存储,前端和日志脱敏处理。如:身份证、手机号、姓名、地址等,尤其是对敏感数据安全性要求较高行业和公司;电商、通信、金融等行业。 一、logback工具包 今天主要讲述后台系统对于敏感数据...
  • 日志脱敏
  • } } 使用场景的话可以是按照项目的路径名来使用自己的appender,比如对自己项目日志脱敏等。 从sl4j 定义的info接口进入到logback真实的接口中最终都会走到这里 public void callAppenders(ILoggingEvent event) { ...
  •   logback-defender是一款基于logback实现的无侵入的日志脱敏工具框架,使用此框架,只需要简单的三步。 功能特性 (默认提供)支持json脱敏器 (默认提供)支持string脱敏器 (默认提供)支持正则脱敏器 支持自定义...
  • 日志的具体实现可以有log4j和logback等,这里我们使用SLF4J作为日志系统的实现。 使用SLF4J 使用idea工具可以安装lombok插件,并引入maven包: <dependency> <groupId>org.projectlombok</...
  • 当我们线上出BUG之后,最常见的定位问题方法就是排查日志文件,所以我们一般都会在开发程序时,在适当的位置输出一些日志信息。 并且有一些日志并不是只打印一些业务字段,可能会将整个对象输出到日志中。比如这样:...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,756
精华内容 1,902
关键字:

日志脱敏