zoukankan      html  css  js  c++  java
  • log4j 日志脱敏处理 + java properties文件加载

    Java 加载Properties 配置文件:

            ResourceBundle bundle = ResourceBundle.getBundle("log4j_filter"); // 不要写扩展名  .properties
            LOG_FILTER_SWITH = bundle.getString("log4j.filter.swith");
            LOG_FILTER_KEYS = bundle.getString("log4j.filter.keys");
    

        // 直接在本类中使用main调用时用 Properties.class.getResourceAsStream("/log4j_filter.properties");
        //Properties p = new Properties();
        //p.load(in);
        //LOG_FILTER_SWITH = p.getProperty("log4j.filter.swith");
        //LOG_FILTER_KEYS = p.getProperty("log4j.filter.keys");

    
    

    日志脱敏实现: 因为只修改了一个log4j类的调用,故日志必须是 

        private final static Logger logger = LogManager.getLogger(SeqConfControl.class);
    1 , 配置文件 log4j_filter.properties (一个参数是脱敏日志开关, 一个参数是要脱敏的关键字)
    2,自定义类  :     org.apache.logging.log4j.spi.Log4jFilter.java 是解析处理日志字符串中的敏感信息;
          log4j原生类: org.apache.logging.log4j.spi.AbstractLogger.java  负责调用自己写的类的方法.
     
    3, 规则   支持k:v  和  k=v  两种形式的数据处理
                     脱敏时v的长度如果大于8,  采用   XXX****XXX的形式脱敏;  如果小于等于8采用  ******* 形式脱敏
     
    效果:
    20:25:02,0197 [INFO ][ ][connect to SFTP . ip:10.0.26.36; port:22;uppath:null;downpath:home/selfftp/selffile/20161209/;username:selfftp;password:********][ :][hulk.frame.sftp.SFtpTools]
    
    21:06:16,0907 [INFO ][ ][接收到一个纯文本消息并开始传入数据库:{"data":"{"state":"0","hostName":"termfrontapp","policyName":"check_SSPM_101","stateName":null,"program":null,"entryId":null,"agentId":null,"msg":"{\"account_num\":\"62294785200****0034\",\"amount\":\"2.00\",\"channel_id\":\"01I\",\"p_trans_code\":\"1017006\",\"card_type\":\"\",\"start_time\":\"1481634017288\",\"end_time\":\"1481634017526\",\"term_id\":\"01159206\",\"ret_code\":\"ZZ\",\"ret_msg\":\"交易失败,请联系发卡方|Transaction failed, please contact the issuser\",\"trans_date_sspm\":\"20161213210017\",\"term_install_addr\":\"10.0.66.8-->10.0.54.198\",\"term_inst_id\":\"238\",\"p_trans_describe\":\"贷款还息\",\"account_id2\":\"\"}","ip":"10.0.26.71","policyId":null}","taskClass":"stateAlarm"}][ :][hulk.sspm.trandataMonitor.util.ConsumerMessageListener] 
    21:06:16,0907 [INFO ][ ][the message to decode: {"account_num":"62294785200****0034","amount":"2.00","channel_id":"01I","p_trans_code":"1017006","card_type":"","start_time":"1481634017288","end_time":"1481634017526","term_id":"01159206","ret_code":"ZZ","ret_msg":"交易失败,请联系发卡方|Transaction failed, please contact the issuser","trans_date_sspm":"20161213210017","term_install_addr":"10.0.66.8-->10.0.54.198","term_inst_id":"238","p_trans_describe":"贷款还息","account_id2":""}]

     log4j_filter.properties:

    # log4j 过滤器配置文件,主要用于系统中敏感字段的脱敏
    
    # true表示打开   false表示关闭
    log4j.filter.swith=true
    # 要脱敏的关键字
    log4j.filter.keys=password,passwd,password1,password2,account_num

     自定义解析类:org.apache.logging.log4j.spi.Log4jFilter.java 

    package org.apache.logging.log4j.spi;
    
    import java.util.ResourceBundle;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    /**
     * 自定义处理 日志中的敏感信息
     * @author yangw1006@163.com
     *
     */
    public class Log4jFilter {
    
        /**
         * 日志脱敏 开关
         */
        private static String LOG_FILTER_SWITH = "false";
        /**
         * 日志脱敏关键字
         */
        private static String LOG_FILTER_KEYS = null; 
        static{
            // 加载配置文件
            try {
                // 直接在本类中使用main调用时用 Properties.class.getResourceAsStream("/log4j_filter.properties");
                //InputStream in =Properties.class.getClassLoader().getResourceAsStream("log4j_filter.properties"); 
                //Properties p = new Properties();
                //p.load(in);
                //LOG_FILTER_SWITH = p.getProperty("log4j.filter.swith");
                //LOG_FILTER_KEYS = p.getProperty("log4j.filter.keys");
                ResourceBundle bundle = ResourceBundle.getBundle("log4j_filter");
                LOG_FILTER_SWITH = bundle.getString("log4j.filter.swith");
                LOG_FILTER_KEYS = bundle.getString("log4j.filter.keys");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        
        
        /**
         * 处理日志字符串,返回脱敏后的字符串
         * @param msg
         * @return
         */
        public static String invokeMsg(final String message){
            
            String msg = new String(message);
            if("true".equals(LOG_FILTER_SWITH)){
                //处理字符串
                if(LOG_FILTER_KEYS!=null && LOG_FILTER_KEYS.length()>0){
                    
                    String[] keyArr = LOG_FILTER_KEYS.split(",");
                    for(String key: keyArr){
                        // 找key
                        int index= -1; 
                        do{
                            index = msg.indexOf(key, index+1);
                            if(index!=-1){ 
                                // 判断key是否为单词字符
                                if(isWordChar(msg,key,index)){
                                    continue;
                                }
                                // 确定是单词无疑....................................
                                // 寻找值的开始位置.................................
                                int valueStart = getValueStartIndex(msg,index + key.length());
                                
                                //查找值的结束位置(逗号,分号)........................
                                int valueEnd = getValuEndEIndex(msg,valueStart);
                                
                                // 对获取的值进行脱敏 
                                String subStr = msg.substring(valueStart, valueEnd);
                                subStr = tuomin(subStr);
                                ///////////////////////////
                                msg = msg.substring(0,valueStart) + subStr + msg.substring(valueEnd);
                            }
                        }while(index!=-1);
                        
                    }
                }
            }
            
            return msg;
        }
        
        
        private static Pattern pattern = Pattern.compile("[0-9a-zA-Z]");
        /**
         * 判断从字符串msg获取的key值是否为单词 , index为key在msg中的索引值
         * @return
         */
        private static boolean isWordChar(String msg,String key, int index){
            
            // 必须确定key是一个单词............................
            if(index!=0){ //判断key前面一个字符
                char preCh = msg.charAt(index-1);
                Matcher match = pattern.matcher(preCh+"");
                if(match.matches()){
                    return true;
                }
            }
            //判断key后面一个字符
            char nextCh = msg.charAt(index+key.length());
            Matcher match = pattern.matcher(nextCh+"");
            if(match.matches()){
                return true;
            }
            
            return false;
            
        }
        
        /**
         * 获取value值的开始位置
         * @param msg 要查找的字符串
         * @param valueStart 查找的开始位置
         * @return
         */
        private static int getValueStartIndex(String msg, int valueStart ){
            // 寻找值的开始位置.................................
            do{
                char ch = msg.charAt(valueStart);
                if(ch == ':' || ch == '='){ // key 与 value的分隔符
                    valueStart ++;
                    ch = msg.charAt(valueStart);
                    if(ch == '"'){
                        valueStart ++;
                    }
                    break;    //找到值的开始位置
                }else{
                    valueStart ++;
                }
                
            }while(true);
            return valueStart;
        }
        
        /**
         * 获取value值的结束位置
         * @return
         */
        private static int getValuEndEIndex(String msg,int valueEnd){
            
            do{
                if(valueEnd == msg.length()){
                    break;
                }
                char ch = msg.charAt(valueEnd);
                
                if(ch == '"'){ // 引号时,判断下一个值是结束,分号还是逗号决定是否为值的结束
                    if(valueEnd+1 == msg.length()){
                        break;
                    }
                    char nextCh = msg.charAt(valueEnd+1);
                    if(nextCh ==';' || nextCh == ','){
                        // 去掉前面的   处理这种形式的数据 "account_num\":\"6230958600001008\"
                        while(valueEnd>0 ){
                            char preCh = msg.charAt(valueEnd-1);
                            if(preCh != '\'){
                                break;
                            }
                            valueEnd--;
                        }
                        break;
                    }else{
                        valueEnd ++;
                    }
                }else if (ch ==';' || ch == ','){
                    break;
                }else{
                    valueEnd ++;
                }
                
            }while(true);
            
            return valueEnd;
        }
        
        private static String tuomin(String submsg){
    
            StringBuffer sbResult = new StringBuffer();
            if(submsg!=null && submsg.length()>0){
                int len = submsg.length();
                if(len > 8){ //8位以上的    隐掉中间4位
                    for(int i = len-1;i>=0;i--){
                        if(len-i<5 || len-i>8){
                            sbResult.insert(0, submsg.charAt(i));
                        }else{
                            sbResult.insert(0, '*');
                        }
                    }
                }else{ //8位以下的全部使用 *
                    for(int i =0;i<len;i++){
                        sbResult.append('*');
                    }
                }
            }
            return sbResult.toString();
        }
        
        public static void main (String[] args) {
            //{\"account_num\":\"6230958600001008\",\"amount\":\"\"
            String msg = "\"account_num\":\"6230958600001008\",\"amount\":\"\"";
            System.out.println(invokeMsg(msg));
            
                    
        }
    }

    log4j原生类 org.apache.logging.log4j.spi.AbstractLogger.java 修改的地方

     # 代码 725行左右的方法
     protected void logMessage(final String fqcn, final Level level, final Marker marker, final String message, final Throwable t) {
            // by yangw
            String invokeMsg = Log4jFilter.invokeMsg(message);
            logMessage(fqcn, level, marker, messageFactory.newMessage(invokeMsg), t);
        }
    ----------- 赠人玫瑰,手有余香     如果本文对您有所帮助,动动手指扫一扫哟   么么哒 -----------


    未经作者 https://www.cnblogs.com/xin1006/ 梦相随1006 同意,不得擅自转载本文,否则后果自负
  • 相关阅读:
    Leetcode-645 Set Mismatch
    2017百度软研(C++)
    二叉树中任意两个节点的最近公共祖先
    不用加减乘除做加法
    一些leetcode算法题
    Leetcode 98. Validate Binary Search Tree
    C++ 通过ostringstream 实现任意类型转string
    Leetcode 215. Kth Largest Element in an Array
    382. Linked List Random Node
    一些基础函数的实现
  • 原文地址:https://www.cnblogs.com/xin1006/p/6172140.html
Copyright © 2011-2022 走看看