Java中关于使用logback做日志脱敏
2017-11-08 23:19
169 查看
最近在研究如何使用logback实现日志脱敏的工作,网上各种查,各种找,终于找到了解决的办法。其实原理知道,就是想找一个最简便的方法而已。那今天咱们就来聊聊如何用Logback创建自定义格式转换符来实现日志脱敏**
脱敏类文件
配置文件
本文使用最简单的脱敏方式进行数据脱敏打印,规则如下:
| 参数 | 脱敏前 | 脱敏后 |
| ——– | ——– | ——– |
| 姓名 | 李丽丽 | 李** |
| 手机号 | 13898701234 | 138****1234 |
| 身份证号 | 111111111111115762 | **************5762 |
| 银行卡号 | 6222600890987671234 | 6222600********1234 |
详细如下:
然后就可以运行代码,输出结果:
{sign=f88898b2677e62f1ad54b9e330c0a27e, idcard=[b]*********2762, realname=%*********************[/b], key=c5d34d4c3c71cc45c88f32b4f13da887, mobile=132****1605, bankcard=622643******7525}
{“reason”:”成功 “,”result”:{“jobid”:”JH2131171027170837443588J6”,”realname”:”李**”,”bankcard”:”622643******7525”,”idcard”:”**************2762”,”mobile”:”132****1605”,”res”:”1”,”message”:”验证成功”},”error_code”:0}
目录:
脱敏类文件
具体代码
配置文件
结束语
文章有不对的地方欢迎大家指正,共同进步,谢谢!!
脱敏类文件
配置文件
脱敏类文件
数据脱敏是指对某些敏感信息通过脱敏规则进行数据的变形,实现敏感隐私数据的可靠保护。 —— [ 百度百科 ]本文使用最简单的脱敏方式进行数据脱敏打印,规则如下:
| 参数 | 脱敏前 | 脱敏后 |
| ——– | ——– | ——– |
| 姓名 | 李丽丽 | 李** |
| 手机号 | 13898701234 | 138****1234 |
| 身份证号 | 111111111111115762 | **************5762 |
| 银行卡号 | 6222600890987671234 | 6222600********1234 |
具体代码
首先定义类:SensitiveDataConverter 继承父类:MessageConverterimport ch.qos.logback.classic.pattern.MessageConverter; import ch.qos.logback.classic.spi.ILoggingEvent; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 敏感信息脱敏处理 * @author AAA */ public class SensitiveDataConverter extends MessageConverter { @Override public String convert(ILoggingEvent event){ // 获取原始日志 String oriLogMsg = event.getFormattedMessage(); // 获取脱敏后的日志 String afterLogMsg = invokeMsg(oriLogMsg); return afterLogMsg; } /** * 日志脱敏开关 */ private static String converterCanRun = "true"; /** * 日志脱敏关键字 */ private static String sensitiveDataKeys = "idcard,realname,bankcard,mobile"; /** * 处理日志字符串,返回脱敏后的字符串 * @param msg * @return */ public String invokeMsg(final String oriMsg){ String tempMsg = oriMsg; if("true".equals(converterCanRun)){ // 处理字符串 if(sensitiveDataKeys != null && sensitiveDataKeys.length() > 0){ String[] keysArray = sensitiveDataKeys.split(","); for(String key: keysArray){ int index= -1; do{ index = tempMsg.indexOf(key, index+1); if(index != -1){ // 判断key是否为单词字符 if(isWordChar(tempMsg, key, index)){ continue; } // 寻找值的开始位置 int valueStart = getValueStartIndex(tempMsg, index + key.length()); // 查找值的结束位置(逗号,分号)........................ int valueEnd = getValuEndEIndex(tempMsg, valueStart); // 对获取的值进行脱敏 String subStr = tempMsg.substring(valueStart, valueEnd); subStr = tuomin(subStr, key); /////////////////////////// tempMsg = tempMsg.substring(0,valueStart) + subStr + tempMsg.substring(valueEnd); } }while(index != -1); } } } return tempMsg; } private static Pattern pattern = Pattern.compile("[0-9a-zA-Z]"); /** * 判断从字符串msg获取的key值是否为单词 , index为key在msg中的索引值 * @return */ private 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; } public static void main(String[] args) { String tempMsg = "{sign=f88898b2677e62f1ad54b9e330c0a27e, idcard=130333198901192762, realname=%E5%BE%90%E5%BD%A6%E5%A8%9C, key=c5d34d4c3c71cc45c88f32b4f13da887, mobile=13210141605, bankcard=6226430106137525}"; String tempMsg1 = "{\"reason\":\"成功 \",\"result\":{\"jobid\":\"JH2131171027170837443588J6\",\"realname\":\"李哪娜\",\"bankcard\":\"6226430106137525\",\"idcard\":\"130333198901192762\",\"mobile\":\"13210141605\",\"res\":\"1\",\"message\":\"验证成功\"},\"error_code\":0}"; SensitiveDataConverter sc = new SensitiveDataConverter(); System.out.println(sc.invokeMsg(tempMsg)); System.out.println(sc.invokeMsg(tempMsg1)); } /** * 获取value值的开始位置 * @param msg 要查找的字符串 * @param valueStart 查找的开始位置 * @return */ private 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 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 == ','){ // 去掉前面的 \ 处理这种形式的数据 while(valueEnd>0 ){ char preCh = msg.charAt(valueEnd-1); if(preCh != '\\'){ break; } valueEnd--; } break; }else{ valueEnd ++; } }else if (ch ==';' || ch == ',' || ch == '}'){ break; }else{ valueEnd ++; } }while(true); return valueEnd; } private String tuomin(String submsg, String key){ // idcard:身份证号, realname:姓名, bankcard:银行卡号, mobile:手机号 if("idcard".equals(key)){ return SensitiveInfoUtils.idCardNum(submsg); } if("realname".equals(key)){ return SensitiveInfoUtils.chineseName(submsg); } if("bankcard".equals(key)){ return SensitiveInfoUtils.bankCard(submsg); } if("mobile".equals(key)){ return SensitiveInfoUtils.mobilePhone(submsg); } return ""; } }
import org.apache.commons.lang.StringUtils; public class SensitiveInfoUtils { /** * [姓名] 只显示第一个汉字,其他隐藏为星号<例子:李**> * * @param fullName * @return */ public static String chineseName(String fullName) { if (StringUtils.isBlank(fullName)) { return ""; } String name = StringUtils.left(fullName, 1); return StringUtils.rightPad(name, StringUtils.length(fullName), "*"); } /** * [身份证号] 显示最后四位,其他隐藏。共计18位或者15位。<例子:*************5762> * * @param idCardNum * @return */ public static String idCardNum(String idCardNum) { if (StringUtils.isBlank(idCardNum)) { return ""; } String num = StringUtils.right(idCardNum, 4); return StringUtils.leftPad(num, StringUtils.length(idCardNum), "*"); } /** * [手机号码] 前三位,后四位,其他隐藏<例子:138******1234> * * @param num * @return */ public static String mobilePhone(String num) { if (StringUtils.isBlank(num)) { return ""; } return StringUtils.left(num, 3).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(num, 4),StringUtils.length(num), "*"), "***")); } /** * [银行卡号] 前六位,后四位,其他用星号隐藏每位1个星号<例子:6222600**********1234> * * @param cardNum * @return */ public static String bankCard(String cardNum) { if (StringUtils.isBlank(cardNum)) { return ""; } return StringUtils.left(cardNum, 6).concat(StringUtils.removeStart(StringUtils.leftPad(StringUtils.right(cardNum, 4), StringUtils.length(cardNum), "*"), "******")); } }
配置文件
代码部分完成之后,我们需要在locback.xml配置文件中增加一行配置:<conversionRule conversionWord="msg" converterClass="com.api.filter.SensitiveDataConverter"> </conversionRule>
详细如下:
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!-- Logback默认配置的采用的步骤 --> <!-- 1. 尝试在 classpath 下查找文件 logback-test.xml; --> <!-- 2. 如果文件不存在,则查找文件 logback.xml; --> <!-- 3. 如果两个文件都不存在,logback 用 BasicConfigurator 自动对自己进行配置,这会导致记录输出到控制台。 --> <!-- 本机环境中只会加载该配置文件,部署服务器时请删除本文件 --> <conversionRule conversionWord="msg" converterClass="com.api.filter.SensitiveDataConverter"> </conversionRule> <!-- 输出控制台 --> <appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <target>System.out</target> <encoder> <Pattern><![CDATA[ [%-5level] [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%logger.%method:%line] -- %msg%n ]]></Pattern> </encoder> </appender> <!-- 时间滚动输出日志 --> <appender name="file—info" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>D:/logs/aaaa.log</file> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> </filter> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>D:/logs/aaaa.%d{yyyy-MM-dd}.log</fileNamePattern> </rollingPolicy> <encoder> <pattern><![CDATA[ [%-5level] [%d{yyyy-MM-dd HH:mm:ss.SSS}] [%t] [%logger.%method:%line] -- %msg%n ]]></pattern> </encoder> </appender> <logger name="dfpay-auto" additivity="false"> <appender-ref ref="INFO_FILE" /> </logger> <root level="INFO"> <appender-ref ref="console" /> </root> </configuration>
然后就可以运行代码,输出结果:
{sign=f88898b2677e62f1ad54b9e330c0a27e, idcard=[b]*********2762, realname=%*********************[/b], key=c5d34d4c3c71cc45c88f32b4f13da887, mobile=132****1605, bankcard=622643******7525}
{“reason”:”成功 “,”result”:{“jobid”:”JH2131171027170837443588J6”,”realname”:”李**”,”bankcard”:”622643******7525”,”idcard”:”**************2762”,”mobile”:”132****1605”,”res”:”1”,”message”:”验证成功”},”error_code”:0}
目录:
脱敏类文件
具体代码
配置文件
结束语
结束语
代码很简单,说白了就是利用logback的Converter,自定义日志格式转换符,然后继承ClassicConverter就可以了 ,当然了还有其他的实现方式,这里就不多写了。文章有不对的地方欢迎大家指正,共同进步,谢谢!!
相关文章推荐
- java日志 -logback的使用和logback.xml详解(转)
- java 使用redis记录logback日志,由自定义Appender与Jedis的使用实现。
- Java日志框架-Spring中使用Logback(Spring/Spring MVC)
- java 使用logback进行日志输出
- Java日志组件logback使用:加载非类路径下的配置文件并设置定时更新
- 【spring boot logback】日志使用自定义的logback-spring.xml文件后,application.properties中关于日志的相关配置还会起作用么
- 【Java】日志知识总结和经常使用组合配置(commons-logging,log4j,slf4j,logback)
- java 日志组件logback的介绍及配置使用方法
- java日志组件logback、slf4j的介绍及配置使用方法(二)
- Java日志框架使用技巧收集(slf4j、jcl、jul、log4j1、log4j2、logback)
- java日志组件logback、slf4j的介绍及配置使用方法(一)
- JAVA使用笔记一(配置日志文件logback以及jetty配置)
- 【Java】日志知识总结和经常使用组合配置(commons-logging,log4j,slf4j,logback)
- Java使用logback记录日志时分级别保存文件
- Java日志框架-logback的介绍及配置使用方法(纯Java工程)(转)
- java日志之slf4j与logback简单使用
- Java日志框架——Logback的使用注意
- 关于使用java中的一些规范
- 如何使用java.util.logging中的Logger来记录日志
- JAVA中使用Log4j日志工具