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;
}
}

    

  

  

  

  

最新文章

  1. 关于Unicode,字符集,字符编码,每个程序员都应该知道的事
  2. myBatis学习笔记
  3. [转] LBYL与EAFP两种防御性编程风格
  4. Java语言中,类所拥有的“孩子”,他们的关系是怎样的
  5. jQuery对表单、表格的操作及更多应用(下:其他应用)
  6. 安装MVC3后没有dbcontext生成器的解决方案
  7. 系统设计 - 使用面向 iOS 的本机插件扩展
  8. poj 2762 强连通缩点+拓扑排序
  9. windows系统dos窗口全屏
  10. rds
  11. java-solr solrj的使用
  12. shell printf命令:怎样格式化输出语句
  13. Altera 在线资源使用
  14. hdu4059 The Boss on Mars 容斥原理
  15. Vue.js路由
  16. winform利用itextsharp.dll实现图片文件转换PDF格式文件
  17. Web API过滤器
  18. MySQL学习笔记:少用Null
  19. 【Xamarin】Visual Studio 2013 Xamarin for Android开发环境搭建与配置&amp;Genymotion
  20. TCP/IP 协议图--TCP/IP 基础

热门文章

  1. accessservice对于难定位的view如何定位
  2. vue 退出登录页面跳转 浏览器localStorage中的值没有改变
  3. javaweb项目启动脚本
  4. spring-boot logback 日志
  5. 虚拟机中 Linux 提示“设备上没有空间”,扩容磁盘
  6. Mac 如何用python下载Mac OS
  7. 查询redis路径,清除redis缓存
  8. SQL server 去掉重复数据
  9. 使用 PSAPI 库枚举进程 EnumProcesses()函数
  10. 解决在宝塔面板IIS服务器上部署svg/woff/woff2字体的问题