.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