C# 试图屏蔽动态对象中的子值
C# Trying to mask child values in a dynamic object
目前我正在向我们的服务添加功能,该功能将接受一个即将被记录的对象,以跟踪和屏蔽该对象中包含的任何敏感字段。
问题是我们可以获得具有不同层的对象。到目前为止,我编写的代码仅处理一个父字段和一个子字段,并使用讨厌的嵌入式 for 循环实现来完成它。
如果我们在要记录的对象中有第三个嵌入式字段层,则根本无法处理。必须有一种更有效的方法来处理动态对象的通用解析,但到目前为止它设法避免了我。
反序列化然后屏蔽对象字段的实际代码如下所示:
private string MaskSensitiveData(string message)
{
var maskedMessage = JsonConvert.DeserializeObject<dynamic>(message);
LoggingProperties.GetSensitiveFields();
for (int i = 0; i < LoggingProperties.Fields.Count(); i++)
{
for (int j = 0; j < LoggingProperties.SubFields.Count(); j++)
{
if (maskedMessage[LoggingProperties.Fields[i]] != null)
{
if (maskedMessage[LoggingProperties.Fields[i]][LoggingProperties.SubFields[j]] != null)
{
maskedMessage[LoggingProperties.Fields[i]][LoggingProperties.SubFields[j]] = MaskField(LoggingProperties.SubFieldLengths[j]);
}
}
}
}
return maskedMessage.ToString(Formatting.None);
}
它使用如下所示的 LoggingProperties class 工作:
public static class LoggingProperties
{
// Constants indicating the number of fields we need to mask at present
private const int ParentFieldCount = 2;
private const int SubFieldCount = 4;
// Constant representing the character we are using for masking
public const char MaskCharacter = '*';
// Parent fields array
public static string[] Fields = new string[ParentFieldCount];
// Subfields array
public static string[] SubFields = new string[SubFieldCount];
// Array of field lengths, each index matching the subfield array elements
public static int[] SubFieldLengths = new int[SubFieldCount];
public static void GetSensitiveFields()
{
// Sensitive parent fields
Fields[0] = "Parent1";
Fields[1] = "Parent2";
// Sensitive subfields
SubFields[0] = "Child1";
SubFields[1] = "Child2";
SubFields[2] = "Child3";
SubFields[3] = "Child4";
// Lengths of sensitive subfields
SubFieldLengths[0] = 16;
SubFieldLengths[1] = 16;
SubFieldLengths[2] = 20;
SubFieldLengths[3] = 3;
}
}
}
目的是为屏蔽方法提供一个特定的字段列表,以便根据我们的系统需求进行扩展或收缩。
嵌套循环方法对我来说似乎有点迂回。感谢任何帮助。
谢谢!
更新:
这是在反序列化调用之前的消息中的父子记录的一个小示例。对于这个例子,假设我试图屏蔽货币 ID(因此在属性中,字段可以这样设置:Parent1 = "Amounts" and Child1 = "CurrencyId"):
{
"Amounts":
{
"Amount":20.0,
"CurrencyId":826
}
}
一个问题的例子是,如果金额被分为英镑和便士:
{
"Amounts":
{
"Amount":
{
"Pounds":20,
"Pence":0
},
"CurrencyId":826
}
}
这将是另一个层和另一个嵌入的 for 循环...但是如果消息中的下一条记录只有两层,我将使其变得过于复杂和困难。
希望这能澄清一些事情 =]
好吧,我真的试过了,但我想不出一个优雅的方法。这是我所做的:
第一次尝试使用反射,但由于所有对象都是 JObject / JToken 类型,所以我无法确定 属性 是对象还是值。
第二次尝试(现在仍然是,如果你能找到一个好方法)更有希望:将 JSON 字符串解析为带有 var data = JObject.Parse(message)
的 JObject 并在递归中枚举其属性方法如下:
void Mask(data)
{
foreach (JToken token in data)
{
if (token.Type == JTokenType.Object)
{
// It's an object, mask its children
Mask(token.Children());
}
else
{
// Somehow mask it but I couldn't figure out to do it with JToken
// Pseudocode, it doesn't actually work:
if (keysToMask.Contains(token.Name))
token.Value = "***";
}
}
}
因为它不适用于 JTokens,所以我已经尝试过对 JProperties 进行同样的操作并且它适用于根对象,但是有一个问题:虽然您可以查看给定的 JProperty 是否是一个对象,但您不能select 它的子对象 JProperty.Children() 再次提供 JToken,但我找不到将其转换为 JProperty 的方法。如果有人知道如何实现它,请post它。
所以我找到的唯一方法是 非常 肮脏的方法:使用正则表达式。这一切都非常优雅 - 但它确实有效。
// Make sure the JSON is well formatted
string formattedJson = JObject.Parse(message).ToString();
// Define the keys of the values to be masked
string[] maskedKeys = {"mask1", "mask2"};
// Loop through each key
foreach (var key in maskedKeys)
{
string original_pattern = string.Format("(\"{0}\": )(\"?[^,\r\n]+\"?)", key);
string masked_pattern = "\"censored\"";
Regex pattern = new Regex(original_pattern);
formatted_json = pattern.Replace(formatted_json, masked_pattern);
}
// Parse the masked string
var maskedMessage = JsonConvert.DeserializeObject<dynamic>(formatted_json);
假设这是您的输入:
{
"val1" : "value1",
"val2" : "value2",
"mask1" : "to be masked",
"prop1" : {
"val3" : "value3",
"val1" : "value1",
"mask2" : "to be masked too",
"prop2" : {
"val1" : "value 1 again",
"mask1" : "this will also get masked"
}
}
}
这是你得到的:
{
"val1": "value1",
"val2": "value2",
"mask1": "censored",
"prop1": {
"val3": "value3",
"val1": "value1",
"mask2": "censored",
"prop2": {
"val1": "value 1 again",
"mask1": "censored"
}
}
}
目前我正在向我们的服务添加功能,该功能将接受一个即将被记录的对象,以跟踪和屏蔽该对象中包含的任何敏感字段。
问题是我们可以获得具有不同层的对象。到目前为止,我编写的代码仅处理一个父字段和一个子字段,并使用讨厌的嵌入式 for 循环实现来完成它。
如果我们在要记录的对象中有第三个嵌入式字段层,则根本无法处理。必须有一种更有效的方法来处理动态对象的通用解析,但到目前为止它设法避免了我。
反序列化然后屏蔽对象字段的实际代码如下所示:
private string MaskSensitiveData(string message)
{
var maskedMessage = JsonConvert.DeserializeObject<dynamic>(message);
LoggingProperties.GetSensitiveFields();
for (int i = 0; i < LoggingProperties.Fields.Count(); i++)
{
for (int j = 0; j < LoggingProperties.SubFields.Count(); j++)
{
if (maskedMessage[LoggingProperties.Fields[i]] != null)
{
if (maskedMessage[LoggingProperties.Fields[i]][LoggingProperties.SubFields[j]] != null)
{
maskedMessage[LoggingProperties.Fields[i]][LoggingProperties.SubFields[j]] = MaskField(LoggingProperties.SubFieldLengths[j]);
}
}
}
}
return maskedMessage.ToString(Formatting.None);
}
它使用如下所示的 LoggingProperties class 工作:
public static class LoggingProperties
{
// Constants indicating the number of fields we need to mask at present
private const int ParentFieldCount = 2;
private const int SubFieldCount = 4;
// Constant representing the character we are using for masking
public const char MaskCharacter = '*';
// Parent fields array
public static string[] Fields = new string[ParentFieldCount];
// Subfields array
public static string[] SubFields = new string[SubFieldCount];
// Array of field lengths, each index matching the subfield array elements
public static int[] SubFieldLengths = new int[SubFieldCount];
public static void GetSensitiveFields()
{
// Sensitive parent fields
Fields[0] = "Parent1";
Fields[1] = "Parent2";
// Sensitive subfields
SubFields[0] = "Child1";
SubFields[1] = "Child2";
SubFields[2] = "Child3";
SubFields[3] = "Child4";
// Lengths of sensitive subfields
SubFieldLengths[0] = 16;
SubFieldLengths[1] = 16;
SubFieldLengths[2] = 20;
SubFieldLengths[3] = 3;
}
}
}
目的是为屏蔽方法提供一个特定的字段列表,以便根据我们的系统需求进行扩展或收缩。
嵌套循环方法对我来说似乎有点迂回。感谢任何帮助。
谢谢!
更新:
这是在反序列化调用之前的消息中的父子记录的一个小示例。对于这个例子,假设我试图屏蔽货币 ID(因此在属性中,字段可以这样设置:Parent1 = "Amounts" and Child1 = "CurrencyId"):
{
"Amounts":
{
"Amount":20.0,
"CurrencyId":826
}
}
一个问题的例子是,如果金额被分为英镑和便士:
{
"Amounts":
{
"Amount":
{
"Pounds":20,
"Pence":0
},
"CurrencyId":826
}
}
这将是另一个层和另一个嵌入的 for 循环...但是如果消息中的下一条记录只有两层,我将使其变得过于复杂和困难。
希望这能澄清一些事情 =]
好吧,我真的试过了,但我想不出一个优雅的方法。这是我所做的:
第一次尝试使用反射,但由于所有对象都是 JObject / JToken 类型,所以我无法确定 属性 是对象还是值。
第二次尝试(现在仍然是,如果你能找到一个好方法)更有希望:将 JSON 字符串解析为带有 var data = JObject.Parse(message)
的 JObject 并在递归中枚举其属性方法如下:
void Mask(data)
{
foreach (JToken token in data)
{
if (token.Type == JTokenType.Object)
{
// It's an object, mask its children
Mask(token.Children());
}
else
{
// Somehow mask it but I couldn't figure out to do it with JToken
// Pseudocode, it doesn't actually work:
if (keysToMask.Contains(token.Name))
token.Value = "***";
}
}
}
因为它不适用于 JTokens,所以我已经尝试过对 JProperties 进行同样的操作并且它适用于根对象,但是有一个问题:虽然您可以查看给定的 JProperty 是否是一个对象,但您不能select 它的子对象 JProperty.Children() 再次提供 JToken,但我找不到将其转换为 JProperty 的方法。如果有人知道如何实现它,请post它。
所以我找到的唯一方法是 非常 肮脏的方法:使用正则表达式。这一切都非常优雅 - 但它确实有效。
// Make sure the JSON is well formatted
string formattedJson = JObject.Parse(message).ToString();
// Define the keys of the values to be masked
string[] maskedKeys = {"mask1", "mask2"};
// Loop through each key
foreach (var key in maskedKeys)
{
string original_pattern = string.Format("(\"{0}\": )(\"?[^,\r\n]+\"?)", key);
string masked_pattern = "\"censored\"";
Regex pattern = new Regex(original_pattern);
formatted_json = pattern.Replace(formatted_json, masked_pattern);
}
// Parse the masked string
var maskedMessage = JsonConvert.DeserializeObject<dynamic>(formatted_json);
假设这是您的输入:
{
"val1" : "value1",
"val2" : "value2",
"mask1" : "to be masked",
"prop1" : {
"val3" : "value3",
"val1" : "value1",
"mask2" : "to be masked too",
"prop2" : {
"val1" : "value 1 again",
"mask1" : "this will also get masked"
}
}
}
这是你得到的:
{
"val1": "value1",
"val2": "value2",
"mask1": "censored",
"prop1": {
"val3": "value3",
"val1": "value1",
"mask2": "censored",
"prop2": {
"val1": "value 1 again",
"mask1": "censored"
}
}
}