我们必须记录我们的Web服务的传入请求和传出响应。这包括每个对象的JSON序列化,以便它们可以存储在数据库中。
某些信息被视为敏感信息(例如社会保险号,信用卡号等),我们无法将这些信息包含在符合PCI合规性的日志中。现在我们手动用占位符值替换值(例如" [PRIVATE]")但这仅适用于字符串属性。某些数据(如出生日期)不会存储为字符串,因此在序列化之前更改属性值时,这不起作用。最大的问题是,在记录之前忘记删除敏感数据太容易了,这是非常不受欢迎的。
为了解决这个问题,我考虑创建一个自定义属性并将其放在属性上,然后让JSON序列化例程在每个属性上查找此属性,如果存在,则将该序列化值替换为占位符,例如& #34; [PRIVATE]"
目前我们正在使用System.Web.Script.Serialization.JavaScriptSerializer进行序列化。显然它对我的自定义属性一无所知。我将如何更改序列化过程,以便使用我的自定义" SensitiveData"属性是否替换为占位符值?我不反对使用不同的序列化程序,但希望我可以利用现有的功能而不是编写自己的功能。
答案 0 :(得分:2)
这是我的解决方案,虽然可能需要稍微调整一下:
我的自定义JsonConverter:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Linq;
using System.Reflection;
public class SensitiveDataJsonConverter : JsonConverter
{
private readonly Type[] _types;
public SensitiveDataJsonConverter(params Type[] types)
{
_types = types;
}
public override bool CanConvert(Type objectType)
{
return _types.Any(e => e == objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var jObject = new JObject();
var type = value.GetType();
foreach (var propertyInfo in type.GetProperties())
{
// We can only serialize properties with getters
if (propertyInfo.CanRead)
{
var sensitiveDataAttribute = propertyInfo.GetCustomAttribute<SensitiveDataAttribute>();
object propertyValue;
if (sensitiveDataAttribute != null)
propertyValue = "[REDACTED]";
else
propertyValue = propertyInfo.GetValue(value);
if (propertyValue == null)
propertyValue = string.Empty;
var jToken = JToken.FromObject(propertyValue, serializer);
jObject.Add(propertyInfo.Name, jToken);
}
}
jObject.WriteTo(writer);
}
这是我的自定义属性:
using System;
[AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)]
public class SensitiveDataAttribute : Attribute
{
}
我们这样使用它:
string serialized;
try
{
serialized = JsonConvert.SerializeObject(value, new SensitiveDataJsonConverter(value.GetType()));
}
catch // Some objects cannot be serialized
{
serialized = $"[Unable to serialize object '{key}']";
}
这是我尝试序列化的测试类:
class Person
{
public Person()
{
Children = new List<Person>();
}
public List<Person> Children { get; set; }
[SensitiveData]
public DateTime DateOfBirth { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[SensitiveData]
public string SocialSecurityNumber { get; set; }
public Person Spouse { get; set; }
}
在添加Spouse和Children属性之前,这似乎很有用,但我得到了NullReferenceException。我将此添加到WriteJson方法中,该方法纠正了问题:
if (propertyValue == null)
propertyValue = string.Empty;
答案 1 :(得分:0)
@DesertFoxAZ在这里有一个很棒的答案。为了扩大答案,我将添加以下代码。没有此代码,PII编辑将仅适用于传递给构造函数的顶级类。如果您的对象属性也应删除一些数据,则还需要以下代码。
下面的代码是根据this SO post.
中的答案进行修改的 private readonly List<Type> _alreadyVisitedTypes = new List<Type>(); //avoid infinite recursion
private void RecursivelySetTypesToOperateOn(Type currentType)
{
if (_alreadyVisitedTypes.Contains(currentType))
{
return;
}
_alreadyVisitedTypes.Add(currentType);
if (currentType.IsClass && currentType.Namespace != "System") //custom defined classes only
{
_types.Add(currentType);
}
foreach (PropertyInfo pi in currentType.GetProperties())
{
if (pi.PropertyType.IsClass)
{
RecursivelySetTypesToOperateOn(pi.PropertyType);
}
}
}