flutter 如何做网站,网站空白页黑链,安卓优化大师官方版本下载,中铁建设集团有限公司背景简介
日志脱敏 是常见的安全需求#xff0c;最近公司也需要将这一块内容进行推进。看了一圈网上的案例#xff0c;很少有既轻量又好用的轮子可以让我直接使用。我一直是反对过度设计的#xff0c;而同样我认为轮子就应该是可以让人拿去直接用的。所以我准备分享两篇博客…背景简介
日志脱敏 是常见的安全需求最近公司也需要将这一块内容进行推进。看了一圈网上的案例很少有既轻量又好用的轮子可以让我直接使用。我一直是反对过度设计的而同样我认为轮子就应该是可以让人拿去直接用的。所以我准备分享两篇博客分别实现两种日志脱敏方案。
方案分析 logback MessageConverter 正则匹配本篇博客主要介绍此方法 优势 侵入性低、工作量极少 只需要修改xml配置文件适合老项目 劣势 效率低会对每一行日志都进行正则匹配检查效率受日志长度影响日志越长效率越低影响日志吞吐量因基于正则匹配 存在错杀风险部分内容难以准确识别 fastjson Filter 注解 工具类下一篇博客介绍 传送门Java日志脱敏二 优势 性能损耗低、效率高、扩展性强精准脱敏适合QPS较高日志吞吐量较大的项目。 劣势 侵入性较高需对所有可能的情况进行脱敏判断存在漏杀风险全靠开发控制 其实还有一种方案基于 工具类配置模式 优势是 工作量低比注解模式低比正则匹配模式高灵活度高性能也好。但是只适合那些新项目如果是老项目大家命名不规范就很难推动整改了。此处不进行扩展。详见项目日志脱敏 logback MessageConverter 正则匹配
流程图解 代码案例
正则匹配日志脱敏工具类
此工具类主要用于实现依据配置的正则匹配规则集进行依次匹配。并提取敏感文本对其执行对应的脱敏策略。大家拿去用可以不做修改
package com.zhibo.log.format;import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*** Author: Zhibo.lv* Description: 正则匹配日志脱敏工具类**/
Component
public class LogSensitiveUtils {// 脱敏日志最大长度超出此长度的日志放弃脱敏直接返回private static Integer SENSITIVE_LOG_MAX_LENGTH 10000;/*** 日志脱敏 获取规则集进行依次匹配* param content 明文日志文本* return 脱敏后的日志文本*/public static String filterSensitive(String content) {try {if (StringUtils.isNotBlank(content) content.length() SENSITIVE_LOG_MAX_LENGTH) {for (Map.EntryString, ListPattern entry : LogSensitiveConstants.SENSITIVE_SEQUENCE.entrySet()) {content filter(content, entry.getKey(), entry.getValue());}}return content;} catch (Exception e) {return content;}}/**** param content 需脱敏字符串* param type 文本类型依据类型可以做不同的脱敏方式* param patterns 该方式下需匹配的正则* return**/public static String filter(String content, String type, ListPattern patterns) {for (Pattern pattern : patterns) {Matcher matcher pattern.matcher(content);StringBuffer sb new StringBuffer();while (matcher.find()) {matcher.appendReplacement(sb, Matcher.quoteReplacement(baseSensitive(matcher.group(), type)));}matcher.appendTail(sb);content sb.toString();}return content;}/*** 依据正则抓去的文本执行对应的脱敏策略* param str 待脱敏的字符串* return*/private static String baseSensitive(String str, String type) {if (StringUtils.isBlank(str)) {return StringUtils.EMPTY;}//通过工厂获取对应类型的脱敏类执行脱敏方法return SensitiveStrategyBuiltInUtil.getStrategy(type).des(str);}
}正则匹配日志脱敏常量
此工具类主要是进行配置需要脱敏的文本的正则。需要大家依据业务调整或新增
package com.zhibo.log.format;import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Pattern;/*** Author: Zhibo* Description: 正则匹配日志脱敏常量**/
public class LogSensitiveConstants {/*** 过滤先后顺序身份证 - 手机号* 顺序原因避免部分业务需求出现可能同时满足多个正则规则的文本大家可以优先提取更长的、更复杂的文本。后处理简单的*/public static final MapString,ListPattern SENSITIVE_SEQUENCE new TreeMapString, ListPattern();/*** 手机号匹配规则集支持配置多个正则规则*/public static final ListPattern SENSITIVE_PHONE_KEY new ArrayListPattern(1);/*** 身份证号码匹配规则集支持配置多个正则规则*/public static final ListPattern SENSITIVE_ID_NO_KEY new ArrayListPattern(1);/*** 手机号正则匹配,11位1开头数字* 瞻前顾后校验符合要求的文本前后均不能为数字 避免误匹配*/public static final String PHONE_REGEX (?!\\d)[1][3-9][0-9]{9}(?!\\d);/*** 身份证号正则匹配 18位数版本* 15位数的身份证号码暂不考虑如果需要自行新增下方正则加入 SENSITIVE_ID_NO_KEY 中* (?!\d)([1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3})(?!\d)* 瞻前顾后校验符合要求的文本前后均不能为数字 避免误匹配*/public static final String ID_NO_REGEX (?!\\d)([1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|X))(?!\\d);static {SENSITIVE_ID_NO_KEY.add(Pattern.compile(ID_NO_REGEX));SENSITIVE_PHONE_KEY.add(Pattern.compile(PHONE_REGEX));}// 脱敏替代字符public static final char STAR *;// 手机号类型脱敏替代字符public static final String PHONE_MASK ****;/** 手机号码脱敏策略 */public static final String STRATEGY_PHONE strategyPhone;/** 身份证号码脱敏策略 */public static final String STRATEGY_ID_NO strategyIdNo;static {//将每一个规则集绑定一个对应的类型SENSITIVE_SEQUENCE.put(STRATEGY_ID_NO, SENSITIVE_ID_NO_KEY);SENSITIVE_SEQUENCE.put(STRATEGY_PHONE, SENSITIVE_PHONE_KEY);}private LogSensitiveConstants() {}
}脱敏策略代码
定义文本脱敏接口 IStrategy
package com.zhibo.log.sensitive.api;/*** Author: Zhibo* Description: 脱敏策略*/
public interface IStrategy {/*** 脱敏* param original 原始内容* return 脱敏后的字符串*/String des(final Object original);
}文本脱敏抽象类进行通用实现 AbstractStringStrategy
package com.zhibo.log.sensitive.core.strategory;import com.zhibo.log.sensitive.api.IStrategy;
import com.zhibo.log.format.LogSensitiveConstants;import java.security.MessageDigest;/*** Author: zhibo* Description: 抽象字符串策略* 支持在脱敏后的文本后面追加明文的MD5加密串方便研发进行日志查询使用*/
public abstract class AbstractStringStrategy implements IStrategy {/*** 获取掩码之前的长度* param original 原始* param chars 字符串* return 结果*/protected abstract int getBeforeMaskLen(Object original, char[] chars);/*** 获取掩码之后的长度* param original 原始* param chars 字符串* return 结果*/protected abstract int getAfterMaskLen(Object original, char[] chars);/*** 针对固定长度的加密直接返回脱敏字符串避免StringBuilder循环拼接* return 脱敏字符串* 如返回null 则通过 {link AbstractStringStrategy#getBeforeMaskLen(Object, char[])} 与 {link AbstractStringStrategy#getAfterMaskLen(Object, char[])}* 进行截取字符串*/protected String getMask(){return null;}/*** 是否需要拼接MD5密文方便日志查询。* return false : 不拼接默认* true : 拼接密文 用于日志查询 格式 [MD5]*/protected Boolean addMD5(){return false;}Overridepublic String des(Object original) {if(original null) {return null;}String strValue original.toString();char[] chars strValue.toCharArray();int beforeMaskLen getBeforeMaskLen(original, chars);int afterMaskLen getAfterMaskLen(original, chars);//范围纠正int maxLen chars.length;beforeMaskLen Math.min(beforeMaskLen, maxLen);afterMaskLen Math.min(afterMaskLen, maxLen);StringBuilder stringBuilder new StringBuilder();//获取明文前缀if(beforeMaskLen 0) {stringBuilder.append(chars, 0, beforeMaskLen);}//获取脱敏字符串String mask getMask();if (null mask){//如未指定脱敏字符串则按规则循环拼接// 中间使用掩码for(int i beforeMaskLen; i chars.length - afterMaskLen; i) {stringBuilder.append(LogSensitiveConstants.STAR);}}else {stringBuilder.append(mask);}//获取明文后缀if(afterMaskLen 0) {stringBuilder.append(chars, chars.length - afterMaskLen, afterMaskLen);}if (addMD5()){addMD5(strValue,stringBuilder);}return stringBuilder.toString();}// MD5加密private void addMD5(String originalString,StringBuilder stringBuilder) {try {MessageDigest md MessageDigest.getInstance(MD5);md.update(originalString.getBytes());byte[] digest md.digest();stringBuilder.append([);for (byte b : digest) {stringBuilder.append(String.format(%02x, b));}stringBuilder.append(]);} catch (Exception e) {e.printStackTrace();}}
}身份证脱敏策略实现 StrategyIdNo
package com.zhibo.log.sensitive.core.strategory;/*** Author: Zhibo* Description: 身份证号脱敏* 脱敏规则保留前6 后4 位其它由星号替换*/
public class StrategyIdNo extends AbstractStringStrategy {Overrideprotected int getBeforeMaskLen(Object original, char[] chars) {return 6;}Overrideprotected int getAfterMaskLen(Object original, char[] chars) {return 4;}
}手机号码脱敏策略实现 StrategyPhone
package com.zhibo.log.sensitive.core.strategory;import com.zhibo.log.format.LogSensitiveConstants;/*** Author: zhibo* Description: 手机号脱敏* 脱敏规则186****8567[MD5]*/
public class StrategyPhone extends AbstractStringStrategy {Overrideprotected int getBeforeMaskLen(Object original, char[] chars) {return 3;}Overrideprotected int getAfterMaskLen(Object original, char[] chars) {return 4;}Overrideprotected String getMask() {return LogSensitiveConstants.PHONE_MASK;}Overrideprotected Boolean addMD5(){return true;}
}logback 消息转换器实现
最关键的方法来啦
package com.zhibo.log.format;import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;/*** Author: Zhibo* Description: 日志脱敏转换器**/
public class SensitiveConverter extends MessageConverter {Overridepublic String convert(ILoggingEvent event){// 获取原始日志String requestLogMsg super.convert(event);// 执行日志脱敏return LogSensitiveUtils.filterSensitive(requestLogMsg);}public SensitiveConverter() {}
}自此我们的工具包也就完成了业务系统需要使用此工具只需要修改resources目录下的logback.xml配置。 !-- 新增或修改原有消息转换器为SensitiveConverter --
conversionRule conversionWordmsgToo converterClasscom.zhibo.log.format.SensitiveConverter /并将文件输出日志的消息内容替换为指定消息转换器的 conversionWord
脱敏效果展示 有请提示
注此方法对日志吞吐量存在影响由于正则需要循环匹配整个日志文本所以正则规则越多日志文本越长耗时越长。如您的应用程序对日志吞吐量要求较高且存在大量超长日志文本请压测后使用。
如配置了logback的异步打印且设置了允许日志丢弃在压测中可能出现因线程池与等待队列均被占满而导致日志丢失情况。下面是我的问题复盘
logback日志异步打印配置如下
appender nameASYNC-FILE classch.qos.logback.classic.AsyncAppenderneverBlocktrue/neverBlock!-- 非阻塞方式运行 如队列满就开始丢弃日志 --queueSize1024/queueSize!-- 等待队列大小 --discardingThreshold0/discardingThreshold!-- 日志队列深度配置0 队列满后丢弃最老的日志 --appender-ref refFILE/
/appender以上配置 为logback线程池工作配置默认线程池 线程数为 10个最大队列长度为1024个。 意味着如果日志产生的速度超过10个线程工作处理日志的速度则无法处理的日志会被写入BlockingQueue 队列当队列满了之后就会导致日志丢失的情况。
继续阅读Java日志脱敏二——fastjson Filter 注解 工具类实现