.Net 使用log4.net进行日志脱敏的研究-.Net使用log4.net进行日志脱敏-.Net 日志脱敏

本文详细介绍了如何在.NET应用中使用log4.net进行日志脱敏,包括自定义PatternLayout的实现、正则表达式的配置以及配置加载和管理的过程。
摘要由CSDN通过智能技术生成

.Net 使用log4.net进行日志脱敏的研究-.Net使用log4.net进行日志脱敏-.Net 日志脱敏

  • 采用的方案

使用自定义的PatternLayout对记录的日志进行转换

  • 步骤如下:
  1. 定义PatternLayout的消息转换器,使用正则匹配的方式对消息进行匹配和脱敏转换
  2. 定义PatternLayout使用自定义的消息转换器
  3. 对于匹配的需要脱敏的字段(正则表达式)进行配置管理,在应用启动阶段进行配置的加载
  4. 在log4.net配置文件的appender.layout节的 type定义使用我们自定义的PatternLayout,(原来是log4.net自带的默认的log4net.Layout.PatternLayout)
具体实现
  • 定义PatternLayout的消息转换器

其中需要脱敏的字段的正则表达式是在应用启动阶段读取配置生成 (DESENSITIVE_REGEX)

上代码:

 /// <summary>
 /// 默认Log4Net的PatternLayout 的PatternConvert
 /// </summary>
 public class DefaultMessagePatternConvert : PatternLayoutConverter
 {
     protected override void Convert(TextWriter writer, LoggingEvent loggingEvent)
     {
         if (loggingEvent.MessageObject is string)
         {
             //启用脱敏配置
             if (Log4netDesensitiveManager.EnabledDesensitive)
             {
                 string message = loggingEvent.MessageObject as string;
                 var result = Log4netDesensitiveManager.DESENSITIVE_REGEX.Replace(message, m => m.Groups[3].Success ? m.Groups[1].Value + m.Groups[2].Value + ReplaceDesensitiveMessage(m.Groups[1].Value, m.Groups[3].Value) : m.Groups[0].Value);
                 writer.Write(result);
                 return;
             }
         }
         loggingEvent.WriteRenderedMessage(writer);
     }

     /// <summary>
     /// 替换掩码
     /// </summary>
     /// <param name="fieldName"></param>
     /// <param name="message"></param>
     /// <returns></returns>
     private string ReplaceDesensitiveMessage(string fieldName, string message)
     {
         var desensitiveConfig = Log4netDesensitiveManager.GetFieldDesensitiveConfig(fieldName);
         if (desensitiveConfig.SaveAroundStartLen > 0)
         {
             if (desensitiveConfig.SaveAroundEndLen > 0)
             {
                 return message.ReplaceDesensitiveAround(desensitiveConfig.SaveAroundStartLen, desensitiveConfig.SaveAroundEndLen);
             }
             else
             {
                 return message.ReplaceDesensitiveAround(desensitiveConfig.SaveAroundStartLen);
             }

         }
         else
         {
             return message.ReplaceDesensitive(desensitiveConfig.SaveMinLen, desensitiveConfig.SaveMaxLen);
         }
     }


 }
  • 定义PatternLayout使用自定义的消息转换器

继承log4.net的PatternLayout类,在构造函数中添加我们自定义的转换器

代码:

/// <summary>
/// 默认Log4Net的PatternLayout,替换log4net.Layout.PatternLayout
/// dou7.WebCore.Log.Log4net.DefaultLog4NetPatternLayout
/// </summary>
public class DefaultLog4NetPatternLayout: PatternLayout
{
    public DefaultLog4NetPatternLayout():base()
    {
        //对message消息进行转换
        this.AddConverter("message", typeof(DefaultMessagePatternConvert));
    }
}

  • 配置的管理和加载

  • 代码:

 /// <summary>
 /// Log4net脱敏配置字段信息
 /// </summary>
 public class DesensitiveConfig
 {
     /// <summary>
     /// 字段名称
     /// </summary>
     public string Name { get; set; }

     /// <summary>
     /// 脱敏最小保留长度,默认4,正数时,代表保留前面的位数,负数时,代表保留后面的位数
     /// </summary>
     public int SaveMinLen { get; set; }

     /// <summary>
     /// 脱敏最多要保留的长度
     /// </summary>
     public int SaveMaxLen { get; set; }

     /// <summary>
     /// 脱敏前面保留长度,默认4
     /// </summary>
     public int SaveAroundStartLen { get; set; }

     /// <summary>
     /// 脱敏后面保留长度,默认4
     /// </summary>
     public int SaveAroundEndLen { get; set; }


 }




配置管理器:

 /// <summary>
 /// Log4net脱敏配置管理
 /// 在启动时执行:Log4netDesensitiveManager.UseDesensitiveConfig(IConfiguration Configuration)
 /// </summary>
 public class Log4netDesensitiveManager
 {
     /// <summary>
     /// 脱敏字段配置字典
     /// </summary>
     public static readonly Dictionary<string, DesensitiveConfig> ConfigMap = new Dictionary<string, DesensitiveConfig>();

     /// <summary>
     /// 脱敏字段配置KEY
     /// </summary>
     private static readonly string DESENSITIVE_CONF_KEY = "Log4net.Desensitive.Fields";

     /// <summary>
     /// 脱敏字段匹配Regex
     /// </summary>
     public static Regex DESENSITIVE_REGEX { get; private set; }

     /// <summary>
     /// 脱敏字段匹配表达式:"(Field1|Field2|FieldN...)("":""|':'|':|"":|:|\b)([^(""|'|,|{|\b)]*)"
     /// </summary>
     public static string PATTERN_STRING { get; private set; }

     /// <summary>
     /// 是否启用日志脱敏,默认不启用
     /// </summary>
     public static bool EnabledDesensitive { get; private set; }

     /// <summary>
     /// 使用脱敏配置,在启动时执行
     /// </summary>
     /// <param name="Configuration"></param>
     public static void UseDesensitiveConfig(IConfiguration Configuration)
     {
         //初始化配置
         InitConfig();
     }

     /// <summary>
     /// 初始化配置
     /// </summary>
     private static void InitConfig()
     {
         Dictionary<string, string> Fields = new Dictionary<string, string>();
         WebCoreApp.Configuration.GetSectionNode(DESENSITIVE_CONF_KEY).Bind(Fields);
         ConfigMap.Clear();

         foreach (var fieldName in Fields.Keys)
         {
             string fieldConfValue = Fields[fieldName];
             DesensitiveConfig desensitiveConf = new DesensitiveConfig();
             desensitiveConf.Name = fieldName;
             string[] fieldConfs = fieldConfValue.Split(',');
             if (fieldConfs.Length > 0)
             {
                 desensitiveConf.SaveMinLen = fieldConfs[0].Trim().IntValue();
             }
             if (fieldConfs.Length > 1)
             {
                 desensitiveConf.SaveMaxLen = fieldConfs[1].Trim().IntValue();
             }
             if (fieldConfs.Length > 2)
             {
                 desensitiveConf.SaveAroundStartLen = fieldConfs[2].Trim().IntValue();
             }
             if (fieldConfs.Length > 3)
             {
                 desensitiveConf.SaveAroundEndLen = fieldConfs[3].Trim().IntValue();
             }
             ConfigMap[fieldName] = desensitiveConf;
         }
         //初始化脱敏字段匹配表达式
         InitPattern();
         Logger.Info("初始化Log4net脱敏配置>>" + ConfigMap.ToJSON());
     }


     /// <summary>
     /// 初始化脱敏字段匹配表达式
     /// </summary>
     private static void InitPattern()
     {
         if (ConfigMap.Keys == null || ConfigMap.Keys.Count == 0)
         {
             EnabledDesensitive = false;
             return;
         }
         EnabledDesensitive = true;
         string fields = ConfigMap.Keys.ToArray().Join("|");
         PATTERN_STRING = "(" + fields + @")("":""|':'|':|"":|:|\b)([^(""|'|,|{|\b)]*)";
         DESENSITIVE_REGEX = new Regex(PATTERN_STRING, RegexOptions.IgnoreCase | RegexOptions.Multiline);
     }

     /// <summary>
     /// 获取脱敏字段掩码配置信息
     /// </summary>
     /// <param name="field"></param>
     /// <returns></returns>
     public static DesensitiveConfig GetFieldDesensitiveConfig(string field)
     {
         return ConfigMap[field];
     }

 }

替换字符串掩码的方法:

/// <summary>
/// 替换字符串掩码
/// </summary>
/// <param name="value">需要替换的字符串</param>
/// <param name="saveMinLen">最小保留长度,默认4,正数时,代表保留前面的位数,负数时,代表保留后面的位数</param>
/// <param name="saveMaxLen">最多要保留的长度</param>
/// <param name="specialChar">掩码字符,默认为*</param>
/// <returns>替换后的结果</returns>
public static string ReplaceDesensitive(this string value, int saveMinLen = 4, int saveMaxLen = -1, char specialChar = '*')
{
    int saveLen = Math.Abs(saveMinLen);
    if (value.Length < 2 || saveLen == 0)
    {
        return value;
    }

    if (saveLen > value.Length)
    {
        saveLen = value.Length - 1;
    }
    if (saveMaxLen > saveLen)
    {
        if (saveMaxLen < value.Length)
        {
            saveLen = saveMaxLen;
        }
    }

    if (saveMinLen > 0)
    {
        string startStr = value.Substring(0, saveLen);
        string specialStr = new string(specialChar, value.Length - saveLen);
        return startStr + specialStr;
    }
    else
    {
        string endStr = value.Substring(value.Length - saveLen);
        string specialStr = new string(specialChar, value.Length - saveLen);
        return specialStr + endStr;
    }

}

/// <summary>
/// 替换字符串前后掩码
/// </summary>
/// <param name="value">需要替换的字符串</param>
/// <param name="saveAroundLen">前后保留长度,默认4</param>
/// <param name="specialChar">特殊字符,默认为*</param>
/// <returns>替换后的结果</returns>
public static string ReplaceDesensitiveAround(this string value, int saveAroundLen = 4, char specialChar = '*')
{
    if (value.Length < 2 || saveAroundLen == 0)
    {
        return value;
    }

    int half = value.Length / 2;

    if (saveAroundLen > half)
    {
        saveAroundLen = half - 1;
    }
    int startLen = saveAroundLen;
    int endLen = saveAroundLen;
    if (saveAroundLen <= 0)
    {
        endLen = 1;
    }
    string startStr = value.Substring(0, startLen);
    string endStr = value.Substring(value.Length - endLen);
    string specialStr = new string(specialChar, value.Length - startLen - endLen);

    return startStr + specialStr + endStr;

}

/// <summary>
/// 替换字符串前后掩码
/// </summary>
/// <param name="value">需要替换的字符串</param>
/// <param name="saveStartLen">前面保留长度</param>
/// <param name="saveEndLen">后面保留长度</param>
/// <param name="specialChar">特殊字符,默认为*</param>
/// <returns></returns>
public static string ReplaceDesensitiveAround(this string value, int saveStartLen, int saveEndLen, char specialChar = '*')
{
    if (value.Length < 3 || saveStartLen + saveEndLen == 0)
    {
        return value;
    }

    if (saveStartLen + saveEndLen > value.Length)
    {
        int half = value.Length / 2;
        saveStartLen = half - 1;
        saveEndLen = half - 1;
        if (half == 1)
        {
            if (saveStartLen > saveEndLen)
            {
                saveStartLen = 1;
            }
            else
            {
                saveEndLen = 1;
            }
        }
    }

    string startStr = value.Substring(0, saveStartLen);
    string endStr = value.Substring(value.Length - saveEndLen);
    string specialStr = new string(specialChar, value.Length - saveStartLen - saveEndLen);

    return startStr + specialStr + endStr;

}

配置格式:

 /**==================配置格式==================
  * "Log4net": {
  *   "Desensitive": {
  *     "Fields": {
  *       "FIELD XXX": "FIELD XXX  VALUE",
  *     }
  *   }
  * }
  * 其中FIELD XXX  VALUE的格式为:SaveMinLen,SaveMaxLen,SaveAroundLen
  * SaveMinLen:脱敏最小保留长度,默认4,正数时,代表保留前面的位数,负数时,代表保留后面的位数
  * SaveMaxLen:脱敏最多要保留的长度
  * SaveAroundStartLen:脱敏前面保留长度,默认4
  * SaveAroundEndLen:脱敏后面保留长度
  *示例:
  * "Log4net": {
  *   "Desensitive": {
  *     "Fields": {
  *       //值格式:脱敏最小保留长度,脱敏最多要保留的长度,脱敏前面保留长度,脱敏后面保留长度
  *       "Name": "1,2,,",
  *       "IdNo": ",,4,",
  *       "Mobiler": ",,3,4",
  *       "Address": "10,15,,"
  *     }
  *   }
  * }
  * 
  */
  • 在应用启动阶段进行配置的加载示例
    Log4netDesensitiveManager.UseDesensitiveConfig(IConfiguration Configuration);

  • 在log4.net配置文件的appender.layout节的 type定义

示例:
原来的:



在这里插入图片描述

使用自定义的PatternLayout配置文件如:




在这里插入图片描述

注意type=dou7.WebCore.Log.Log4net.DefaultLog4NetPatternLayout

  • 5
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北海山人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值