1.按正则表达式脱敏处理

  参考:

    https://www.cnblogs.com/htyj/p/12095615.html

    http://www.heartthinkdo.com/?p=998

  站在两位创作者的肩膀上,我很不要脸的将他们的内容做了下整合,捂脸中...

  一般处理都是继承PatternLayout实现自己的处理方式,上代码

  注意:这里隐藏处理只是针对数字类型的字符串做了简单的编码替换处理,可用其他通用加密方式进行替代。

package com.demo.log;

import ch.qos.logback.classic.PatternLayout;
import ch.qos.logback.classic.spi.ILoggingEvent;
import org.apache.commons.lang.StringUtils;
import org.springframework.util.CollectionUtils; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern; /**
* 对敏感信息进行掩盖。
* 1.实现原理
* 对产生的日志信息,进行正则匹配和替换。
* <p>
* 2.目前包括如下类型的信息:银行卡号、电话、身份证和邮箱。
* <p>
* 3.如何进行扩展新的正则类型
* (1)在PatternType枚举中新增一个正则
* (2)extractMatchesByType对新增的正则做处理
* (3)maskByType对新增的正则做处理
* <p>
*/
public class MaskingPatternLayout extends PatternLayout { /**
* 匹配的所有正则
*/
private Map<PatternType, Pattern> patternsMap = new HashMap<>();
private static final String KEY = "GepqwLZYdk"; public MaskingPatternLayout() {
loadPatterns();
} @Override
public String doLayout(ILoggingEvent event) {
String message = super.doLayout(event);
if (CollectionUtils.isEmpty(patternsMap)) {
return message;
}
// 处理日志信息
try {
return process(message);
} catch (Exception e) {
// 这里不做任何操作,直接返回原来message
return message;
}
} /**
* 加载正则表达式,生成相应的Pattern对象。
*/
private void loadPatterns() {
for (PatternType patternType : PatternType.values()) {
Pattern pattern = Pattern.compile(patternType.getRegex());
patternsMap.put(patternType, pattern);
}
} /**
* 替换信息
*
* @param message
* @return
*/
public String process(String message) {
for (PatternType key : patternsMap.keySet()) {
// 1.生成matcher
Pattern pattern = patternsMap.get(key);
Matcher matcher = pattern.matcher(message); // 2.获取匹配的信息
Set<String> matches = extractMatchesByType(matcher); // 3.掩盖匹配的信息
if (!CollectionUtils.isEmpty(matches)) {
message = maskByType(key, message, matches);
}
} return message;
} /**
* 根据正则类型来做相应的提取
*
* @param matcher
* @return
*/
private Set<String> extractMatchesByType(Matcher matcher) {
// 邮箱、电话、银行卡、身份证都是通过如下方法进行提取匹配的字符串
return extractDefault(matcher); } /**
* 1.提取匹配的所有字符串中某一个分组
* group(0):表示不分组,整个表达式的值
* group(i),i>0:表示某一个分组的值
* <p>
* 2.使用Set进行去重
*
* @param matcher
* @return
*/
private Set<String> extractDefault(Matcher matcher) {
Set<String> matches = new HashSet<>();
int count = matcher.groupCount(); while (matcher.find()) {
if (count == 0) {
matches.add(matcher.group());
continue;
}
for (int i = 1; i <= count; i++) {
String match = matcher.group(i);
if (null != match) {
matches.add(match);
}
} } return matches;
} /**
* 根据不同类型敏感信息做相应的处理
*
* @param key
* @param message
* @return
*/
private String maskByType(PatternType key, String message, Set<String> matchs) {
if (key == PatternType.ID_CARD) {
return maskIdCard(message, matchs);
} else if(key == PatternType.BANK_CARD){
return maskBankcard(message, matchs);
} else if(key == PatternType.PHONE_NUMBER){
return maskPhone(message, matchs);
} else{
return message;
} } /**
* 掩盖数字类型信息
*
* @param message
* @param matches
* @return
*/
private String maskIdCard(String message, Set<String> matches) { for (String match : matches) {
// 1.处理获取的字符
String matchProcess = baseSensitive(match, 4, 4);
// 2.String的替换
message = message.replace(match, matchProcess);
}
return message;
} private String maskBankcard(String message, Set<String> matches) {
for (String match : matches) {
// 1.处理获取的字符
String matchProcess = baseSensitive(match, 3, 3);
// 2.String的替换
message = message.replace(match, matchProcess);
}
return message;
} private String maskPhone(String message, Set<String> matches) {
for (String match : matches) {
// 1.处理获取的字符
String matchProcess = baseSensitive(match, 2, 2);
// 2.String的替换
message = message.replace(match, matchProcess);
}
return message;
} private static String baseSensitive(String str, int startLength, int endLength) {
if (StringUtils.isBlank(str)) {
return "";
}
String replacement = str.substring(startLength,str.length()-endLength);
StringBuffer sb = new StringBuffer();
for(int i=0;i<replacement.length();i++) {
char ch;
if(replacement.charAt(i)>='0' && replacement.charAt(i)<='9') {
ch = KEY.charAt((int)(replacement.charAt(i) - '0'));
}else {
ch = replacement.charAt(i);
}
sb.append(ch);
}
return StringUtils.left(str, startLength).concat(StringUtils.leftPad(StringUtils.right(str, endLength), str.length() - startLength, sb.toString()));
} private static String decrypt(String str, int startLength, int endLength) {
if (StringUtils.isBlank(str)) {
return "";
}
String replacement = str.substring(startLength,str.length()-endLength);
StringBuffer sb = new StringBuffer();
for(int i=0;i<replacement.length();i++) {
int index = KEY.indexOf(replacement.charAt(i));
if(index != -1) {
sb.append(index);
}else {
sb.append(replacement.charAt(i));
}
}
return StringUtils.left(str, startLength).concat(StringUtils.leftPad(StringUtils.right(str, endLength), str.length() - startLength, sb.toString()));
} /**
* 定义敏感信息类型
*/
private enum PatternType {
// 1.手机号共11位,模式为: 13xxx,,14xxx,15xxx,17xxx,18xx
PHONE_NUMBER("手机号", "[^\\d](1[34578]\\d{9})[^\\d]"),
// 2.银行卡号,包含16位和19位
BANK_CARD("银行卡", "[^\\d](\\d{16})[^\\d]|[^\\d](\\d{19})[^\\d]"),
// 3.邮箱
EMAIL("邮箱", "[A-Za-z_0-9]{1,64}@[A-Za-z1-9_-]+.[A-Za-z]{2,10}"),
// 4. 15位(全为数字位)或者18位身份证(17位位数字位,最后一位位校验位)
ID_CARD("身份证", "[^\\d](\\d{15})[^\\d]|[^\\d](\\d{18})[^\\d]|[^\\d](\\d{17}X)"); private String description;
private String regex; private PatternType(String description, String regex) {
this.description = description;
this.regex = regex;
} public String getDescription() {
return description;
} public void setDescription(String description) {
this.description = description;
} public String getRegex() {
return regex;
} public void setRegex(String regex) {
this.regex = regex;
}
} }

  logback.xml:

    <property name="rolling.pattern" value="%d{yyyy-MM-dd}"/>
<property name="layout.pattern" value="%-5p %d [%t] %c{50} > %m%n"/> <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="com.demo.log.MaskingPatternLayout">
<pattern>${layout.pattern}</pattern>
</layout>
</encoder>
</appender>

  2.按指定字段脱敏处理

  参考:https://gitee.com/cqdevops/diary_desensitization

  注意:这种方式是需要一定前提条件的,日志内容的格式有限制(如json串或者{字段名=“”}),具体可以到参考文章看看,然后可以在源码的基础上自己调整。

       说明一下,这里是指cardId跟idNo这两者的字段名的内容按idCardNo类型处理,realName字段名的内容按照trueName方式处理,一开始我也看得云里雾里。 

      

    

    下载源码后,导入工程后,maven install到本地仓库,不能直接使用install后的jar,因为它没有把依赖包打进去,引用的话会报ClassNotFound

      在你maven工程下的pom.xml引用,文章中引用的groupId是错误的,所以会一直引不到:

       <dependency>
<groupId>com.gitee.cqdevops</groupId>
<artifactId>desensitization-logback</artifactId>
<version>1.1.1</version>
</dependency>

     针对源码做了一些微调,对字段内容开始的tag做了一下处理,但可能不是最优的处理:

package com.gitee.cqdevops.desensitization.pattern;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern; public class KeywordConverter extends BaseConverter { private static Pattern pattern = Pattern.compile("[0-9a-zA-Z]"); @Override
public String invokeMsg(final String oriMsg){
String tempMsg = oriMsg;
try {
if("true".equals(converterCanRun)){
if(!keywordMap.isEmpty()){
Set<String> keysArray = keywordMap.keySet();
for(String key: keysArray){
int index = -1;
int i = 0;
do{
index = tempMsg.indexOf(key, index + 1);
if(index != -1){
if(isWordChar(tempMsg, key, index)){
continue;
}
Map<String,Object> valueStartMap = getValueStartIndex(tempMsg, index + key.length()); int valueStart = (int)valueStartMap.get("valueStart");
char tag = (char)valueStartMap.get("tag");
int valueEnd = getValueEndEIndex(tempMsg, valueStart,tag);
// 对获取的值进行脱敏
String subStr = tempMsg.substring(valueStart, valueEnd);
subStr = facade(subStr, keywordMap.get(key));
tempMsg = tempMsg.substring(0,valueStart) + subStr + tempMsg.substring(valueEnd);
i++;
}
}while(index != -1 && i < depth);
}
}
}
} catch (Exception e) {
return tempMsg;
}
return tempMsg;
} /**
* 判断key是否为单词内字符
* @param msg 待检查字符串
* @param key 关键字
* @param index 起始位置
* @return 判断结果
*/
private boolean isWordChar(String msg, String key, int index){
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;
} private Map<String,Object> getValueStartIndex(String msg, int valueStart ){
Map<String,Object> map= new HashMap<>();
do{
char ch = msg.charAt(valueStart);
if(ch == ':' || ch == '='){
valueStart ++;
ch = msg.charAt(valueStart);
if(ch == '"' || ch =='\''){
valueStart ++;
map.put("valueStart",valueStart);
map.put("tag",ch);
}
break;
}else{
valueStart ++;
}
}while(true); return map;
} private int getValueEndEIndex(String msg, int valueEnd,char tag){
do{
if(valueEnd == msg.length()){
break;
}
char ch = msg.charAt(valueEnd); if(ch == tag){
if(valueEnd + 1 == msg.length()){
break;
}
char nextCh = msg.charAt(valueEnd + 1);
if(nextCh == ';' || nextCh == ','|| nextCh == '}'){
while(valueEnd > 0 ){
char preCh = msg.charAt(valueEnd - 1);
if(preCh != '\\'){
break;
}
valueEnd--;
}
break;
}else{
valueEnd ++;
}
} else{
valueEnd ++;
}
}while(true); return valueEnd;
} /**
* 寻找key对应值的开始位置
* @param msg 待检查字符串
* @param valueStart 开始寻找位置
* @return key对应值的开始位置
*/
// private int getValueStartIndex(String msg, int valueStart ){
// do{
// char ch = msg.charAt(valueStart);
// if(ch == ':' || ch == '='){
// valueStart ++;
// ch = msg.charAt(valueStart);
// if(ch == '"'){
// valueStart ++;
// }
// break;
// }else{
// valueStart ++;
// }
// }while(true);
//
// return valueStart;
// } /**
* 寻找key对应值的结束位置
* @param msg 待检查字符串
* @param valueEnd 开始寻找位置
* @return key对应值的结束位置
*/
private int getValueEndEIndex(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 == ','|| 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;
}
}

    

  

  

  

  

logback 日志脱敏处理的更多相关文章

  1. java 日志脱敏框架 sensitive,优雅的打印脱敏日志

    问题 为了保证用户的信息安全,敏感信息需要脱敏. 项目开发过程中,每次处理敏感信息的日志问题感觉很麻烦,大部分都是用工具类单独处理,不利于以后统一管理,很不优雅. 于是,就写了一个基于 java 注解 ...

  2. Logback日志系统配置攻略

    logback是log4j作者推出的新日志系统,原生支持slf4j通用日志api,允许平滑切换日志系统,并且对简化应用部署中日志处理的工作做了有益的封装. 官方地址为:http://logback.q ...

  3. lombok+slf4j+logback SLF4J和Logback日志框架详解

    maven 包依赖 <dependency> <groupId>org.projectlombok</groupId> <artifactId>lomb ...

  4. mybatis用logback日志不显示sql的解决办法

    mybatis用logback日志不显示sql的解决方法 1.mybatis-config.xml的设定 关于logimpl的设定值还不支持logback,如果用SLF4J是不好用的. 这是官方文档的 ...

  5. log4j 日志脱敏处理 + java properties文件加载

    Java 加载Properties 配置文件: ResourceBundle bundle = ResourceBundle.getBundle("log4j_filter"); ...

  6. Logback日志配置的简单使用

    Logback介绍 Logback是由log4j创始人设计的又一个开源日志组件.logback当前分成三个模块:logback-core,logback- classic和logback-access ...

  7. 在SpringBoot中添加Logback日志处理

    前言 SpringBoot项目中在官方文档中说明,默认已经依赖了一些日志框架.而其中推荐使用的就是Logback,所以这一次我将在我的模版中加入Logback日志的配置,说明一下,SpringBoot ...

  8. 剑指架构师系列-spring boot的logback日志记录

    Spring Boot集成了Logback日志系统. Logback的核心对象主要有3个:Logger.Appender.Layout 1.Logback Logger:日志的记录器 主要用于存放日志 ...

  9. Logback日志基础配置以及自定义配置

    Logback日志基础配置 logback日志配置有很多介绍,但是有几个非常基础的,容易忽略的.下面是最简单的一个配置,注意加粗的描述 <?xml version="1.0" ...

  10. shell实战之日志脱敏

    本次实战目标为日志脱敏,将日志目录内的所有文件进行处理,凡是涉及到卡号和密码的信息,一律以“*”号替代,要替代的内容都从对应的标签内获取,本脚本执行目录 drwxr-xr-x 5 root root ...

随机推荐

  1. Python3中Super函数的使用

    Super函数用法 主要用于调用父类函数 代码演示 class A: def __init__(self): self.n = 2 print('此时执行A的自定义函数,self的n值为',self. ...

  2. Word19 撰写企业质量管理论文office真题

    1.看到题目要求:打开考试文件下的素材文档"WPS.docx"文件,后续操作均基于此文件,否则不得分. 2.这一步的操作非常简单,打开文件目录进行双击打开即可完成操作. 3.看到题 ...

  3. NSIS 自定义界面,下载并安装Net.Framework4.8

    以 ScreenToGif 这款软件为例,详细讲解如何在安装的过程中检测并下载net包进行安装. 前言 1.ScreenToGif 是一款开源的截屏软件,依赖于Net.Framework环境 2.本文 ...

  4. 炫酷 css实现水波纹

    携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第23天,点击查看活动详情 ui设计的元素有时候需要有一些动画效果,可以直接用css动画来实现. 实现一个中心圆向四周有水波纹的效果 ...

  5. 2022-05-19内部群每日三题-清辉PMP

    1.项目经理与项目相关方开会,获得关于如何最好地向施工现场输送用品的信息和知识.这使用的是哪种沟通方法类型? A.交互式沟通 B.内部沟通 C.拉式沟通 D.推式沟通 2.一个国际团队被分配到一个项目 ...

  6. Go语言中超过1000个线程panic

    1.问题描述 2.实验 3.原理 4.解释 Close太多,Close在Windows上阻塞型的可能会新创建线程,而Linux上是非阻塞型不会新创建线程.

  7. ts get和set

    class User { // get.set方法的成员变量命名时建议在前面加 _ private _fullName: string; //get 的用法 get fullName(): strin ...

  8. locust 检查点、集合点、参数化设置。

    1.参数化 方式1:循环取数据,数据可以重复使用. 所有并发虚拟用户共享同一份测试数据,各虚拟用户在数据列表中循环取值. 例如,模拟10个用户并发请求网页,总共有4个URL地址,每个虚拟用户都会依次循 ...

  9. Delphi获取程序版本号

    参考: http://www.delphitop.com/html/hanshu/4627.html procedure GetVersionInfo(const FileName:string; v ...

  10. linux与windows互通

    https://www.cnblogs.com/zhouby/p/10724149.html