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"
    }
  }
}