.NET Core返回值处理(手机身份证号码脱敏)的三种方式


在已有业务下面添加脱敏需求,不允许污染源方法,所以只能对返回值进行处理,这里列举两种方法,自定义序列化和过滤器。

自定义序列化应对所有用到该实体类的情况,过滤器应对只需要某一个或几个请求脱敏的情况;

如果仅仅处理脱敏,直接使用通用脱敏工具类即可。

通用脱敏工具类代码如下

 1 public static class DesensitizationUtil
 2 {
 3     /// <summary>
 4     /// 将传入的字符串中间部分字符替换成特殊字符
 5     /// </summary>
 6     /// <param name="value">需要替换的字符串</param>
 7     /// <param name="startLen">前保留长度,默认4</param>
 8     /// <param name="subLen">要替换的长度,默认4</param>
 9     /// <param name="specialChar">特殊字符,默认为*</param>
10     /// <returns>替换后的结果</returns>
11     public static string ReplaceWithSpecialChar(this string value, int startLen = 4, int subLen = 4, char specialChar = '*')
12     {
13         if (value.Length <= startLen + subLen) return value;
14       
15         string startStr = value.Substring(0, startLen);
16         string endStr = value.Substring(startLen + subLen);
17         string specialStr = new string(specialChar, subLen);
18 
19         return startStr + specialStr + endStr;
20     }
21 }

 


方式一:JsonConverter

JsonConverter的方式对实体类进行修饰,所有用到该实体类的地方都会按照指定的格式输出。我们只需要输出时脱敏,所以将CanRead设置为false,关闭反序列化,同时ReadJson不填写逻辑代码;CanWrite设置为true,WriteJson中将返回值value脱敏后写入。

引用:

1 using Newtonsoft.Json;

具体代码:

 1 public class DesensitizationConvter : JsonConverter
 2 {
 3     //关闭反序列化
 4     public override bool CanRead => false;
 5     //开启自定义序列化
 6     public override bool CanWrite => true;
 7     public override bool CanConvert(Type objectType)
 8     {
 9         return true;
10     }
11     private readonly int StartLen;
12     private readonly int SubLen;
13     private readonly char SpecialChar;
14     /// <summary>
15     /// 默认前保留长度4,脱敏长度4,特殊字符为*
16     /// </summary>
17     public DesensitizationConvter()
18     {
19         StartLen = 4;
20         SubLen = 4;
21         SpecialChar = '*';
22     }
23     /// <summary>
24     /// 根据传入的长度进行脱敏
25     /// </summary>
26     /// <param name="startLen">前保留长度</param>
27     /// <param name="subLen">脱敏长度</param>
28     public DesensitizationConvter(int startLen, int subLen) : this()
29     {
30         StartLen = startLen;
31         SubLen = subLen;
32     }
33     /// <summary>
34     /// 根据传入的长度及特殊字符进行脱敏
35     /// </summary>
36     /// <param name="startLen">前保留长度</param>
37     /// <param name="subLen">脱敏长度</param>
38     /// <param name="specialChar">替换的特殊字符</param>
39     public DesensitizationConvter(int startLen, int subLen, char specialChar) : this(startLen, subLen)
40     {
41         SpecialChar = specialChar;
42     }
43 
44     public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
45     {
46         throw new NotImplementedException();
47     }
48 
49     public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
50     {
51         if (value != null)
52         {
53             writer.WriteValue(value.ToString().ReplaceWithSpecialChar(StartLen, SubLen, SpecialChar));
54         }
55     }
56 }

然后在实体类需要脱敏的属性上面加上修饰:

1 [JsonConverter(typeof(DesensitizationConvter))]
2 public string Phone { get; set; }
3 
4 [JsonConverter(typeof(DesensitizationConvter), 3, 4)]
5 public string Phone2 { get; set; }
6 
7 [JsonConverter(typeof(DesensitizationConvter), 4, 10, '*')]
8 public string IDCard { get; set; }

 WebApi请设置序列化工具类为Newtonsoft.Json,直接序列化方式为JsonConvert.SerializeObject(model)。


方式二:过滤器(JObject遍历)

过滤器Filter的方式相比较于Json序列化更加灵活,哪个请求需要脱敏,就加上修饰,不影响其他请求结果。

引用:

1 using Newtonsoft.Json.Linq;
2 using System.Linq;

构造函数中为需要脱敏的对象赋值,这里为了更加灵活,使用的是动态传参,然后处理成二维数组,支持多个属性、自定义脱敏位置的传参方式,包含需要脱敏的字段名称、前保留长度、脱敏长度、脱敏的特殊符号。

具体代码:

 1 public class DesensitizationAttribute : Attribute, IActionFilter
 2 {
 3     public string[][] Desensitizations { get; set; }
 4     // 动态传参,可以多个字段脱敏
 5     public DesensitizationAttribute(params string[] desensitizations)
 6     {
 7         Desensitizations = new string[desensitizations.Length][];
 8         for (int i = 0; i < desensitizations.Length; i++)
 9         {
10             //处理中文逗号
11             desensitizations[i] = desensitizations[i].Replace("", ",");
12             string[] str = desensitizations[i].Split(',');
13             //准备脱敏的参数
14             switch (str.Count())
15             {
16                 case 1:
17                     Desensitizations[i] = new string[4] { str[0].Trim(), "4", "4", "*" };
18                     break;
19                 case 3:
20                     Desensitizations[i] = new string[4] { str[0].Trim(), str[1].Trim(), str[2].Trim(), "*" };
21                     break;
22                 case 4:
23                     Desensitizations[i] = new string[4] { str[0].Trim(), str[1].Trim(), str[2].Trim(), str[3].Trim() };
24                     break;
25                 default:
26                     break;
27             }
28         }
29     }
30 
31     public void OnActionExecuting(ActionExecutingContext context)
32     {
33 
34     }
35 
36     public void OnActionExecuted(ActionExecutedContext context)
37     {
38         if (context.Result != null && context.Result is ObjectResult)
39         {
40             ObjectResult obj = context.Result as ObjectResult;
41             //转换成JObject
42             JObject jo = JObject.FromObject(obj.Value);
43             //如果已知需要替换的字段层级关系,直接查询脱敏即可,例如传入A.B.Phone
44             //foreach (var item in Desensitizations)
45             //{
46             //    if (jo.SelectToken(item[0]) != null)
47             //    {
48             //        string newStr = jo.SelectToken(item[0]).ToString().ReplaceWithSpecialChar(Convert.ToInt32(item[1]), Convert.ToInt32(item[2]), Convert.ToChar(item[3]));
49             //        jo.SelectToken(item[0]).Replace(newStr);
50             //    }
51             //}
52 
53             // 如果不知道层级,使用下面方法遍历JObject
54             foreach (JToken item in jo.Values()){
55                 CommonReplaceConvter(item);
56             }
57 
58             context.Result = new ObjectResult(jo);
59         }
60     }
61 
62     /// <summary>
63     /// 递归遍历JToken脱敏
64     /// </summary>
65     /// <param name="jt"></param>
66     public void CommonReplaceConvter(JToken jt)
67     {
68         if (jt == null) return;
69 
70         if (jt.HasValues && jt.Values().Count() > 0)
71         {
72             foreach (JToken item in jt.Values())
73             {
74                 CommonReplaceConvter(item);
75             }
76         }
77         else
78         {
79             string[] str = Desensitizations.FirstOrDefault(t => t[0] == jt.Path.Split('.').Last());
80 
81             if (str != null)
82             {
83                 string result = jt == null ? "" : jt.ToString().ReplaceWithSpecialChar(Convert.ToInt32(str[1]), Convert.ToInt32(str[2]), Convert.ToChar(str[3]));
84                 jt.Replace(result);
85             }
86         }
87     }
88 }

  最后在Controller上添加修饰,第一个参数表示Phone字段按照默认格式脱敏;第二个参数表示Phone2字段前面保留3位,脱敏长度为4,特殊字符为默认的*;第三个参数表示IDCard字段前面保留4位,脱敏长度为10,特殊字符为*(也可以改成自定义的字符):

[Desensitization("Phone", "Phone2,3,4", "IDCard,4,10,*")]

 


方式三:过滤器(反射)

引用:

1 using System.Collections;
2 using System.Linq;
3 using System.Reflection;

此方法与第二种方法类似,只是替换的方式不同,这里用的是反射。

构造函数中为需要脱敏的对象赋值,这里用的是二维数组,包含需要脱敏的字段名称、前保留长度、脱敏长度、脱敏的特殊符号。具体传值方法请往下看。

具体代码:

 1 public class DesensitizationAttribute : Attribute, IActionFilter
 2 {
 3     public string[][] Desensitizations { get; set; }
 4     public DesensitizationAttribute(params string[] desensitizations)
 5     {
 6         Desensitizations = new string[desensitizations.Length][];
 7         for (int i = 0; i < desensitizations.Length; i++)
 8         {
 9             desensitizations[i] = desensitizations[i].Replace("", ",");
10             string[] str = desensitizations[i].Split(',');
11             switch (str.Count())
12             {
13                 case 1:
14                     Desensitizations[i] = new string[4] { str[0].Trim(), "4", "4", "*" };
15                     break;
16                 case 3:
17                     Desensitizations[i] = new string[4] { str[0].Trim(), str[1].Trim(), str[2].Trim(), "*" };
18                     break;
19                 case 4:
20                     Desensitizations[i] = new string[4] { str[0].Trim(), str[1].Trim(), str[2].Trim(), str[3].Trim() };
21                     break;
22                 default:
23                     break;
24             }
25         }
26     }
27 
28     public void OnActionExecuting(ActionExecutingContext context)
29     {
30 
31     }
32 
33     public void OnActionExecuted(ActionExecutedContext context)
34     {
35         if (context.Result != null && context.Result is ObjectResult)
36         {
37             ObjectResult obj = context.Result as ObjectResult;
38 
39             GetInfoPropertys(obj.Value);
40             context.Result = obj;
41         }
42     }
43 
44     /// <summary>
45     /// 反射的方式递归脱敏
46     /// </summary>
47     /// <param name="obj">需要脱敏的对象</param>
48     public void GetInfoPropertys(object obj)
49     {
50         if (obj == null) return;
51         Type type = obj.GetType();
52         if (type.IsGenericType)
53         {
54             //如果是List,需要遍历
55             if (obj is ICollection Ilist)
56             {
57                 foreach (object o in Ilist)
58                 {
59                     GetInfoPropertys(o);
60                 }
61             }
62             return;
63         }
64         foreach (PropertyInfo property in type.GetProperties())
65         {
66             object value = property.GetValue(obj, null);
67             if (property.PropertyType.IsValueType || property.PropertyType.Name.StartsWith("String"))
68             {
69                 string[] str = Desensitizations.FirstOrDefault(t => t[0] == property.Name);
70                 if (str != null)
71                 {
72                     string result = value == null ? "" : value.ToString().ReplaceWithSpecialChar(Convert.ToInt32(str[1]), Convert.ToInt32(str[2]), Convert.ToChar(str[3]));
73                     property.SetValue(obj, result, null);
74                 }
75             }
76             else
77             {
78                 GetInfoPropertys(value);
79             }
80         }
81     }
82 }

 同样在Controller上添加修饰:

[Desensitization("Phone", "Phone2,3,4", "IDCard,4,10,*")]

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM