布莱恩·罗杰的自定义合约解析器 https://stackoverflow.com/a/29240043/3744182使用自定义IValueProvider https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_Serialization_IValueProvider.htm注入逻辑来加密和解密字符串值属性。您想将其概括为加密和解密任何类型的财产通过对 JSON 进行嵌套序列化或反序列化,然后对生成的 JSON 字符串进行加密或解密。不幸的是,自定义值提供者并不真正适合此目的。值提供者的职责是从数据库中设置和获取值。它的设计目的不是对该值进行任意复杂的转换,也不是进行嵌套序列化。 (当前JsonSerializer
甚至没有传递给价值提供者。)
代替价值提供者的是custom JsonConverter https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm可以由联系人解析器应用来执行必要的嵌套序列化和加密。 AJsonConverter
可以访问当前的序列化器,其职责包括将 JSON 转换为最终数据模型,因此它更适合此任务。
首先,定义以下属性和接口:
public interface IEncryptionService
{
/// <summary>
/// Encrypt the incoming string into another Utf8 string using whatever algorithm, password and salt are appropiate.
/// </summary>
/// <param name="input">The string to be encrypted</param>
/// <returns>The encypted string</returns>
public string Encrypt(string input);
/// <summary>
/// Decrypt the incoming string into another Utf8 string using whatever algorithm, password and salt are appropiate.
/// </summary>
/// <param name="input">The encrypted string</param>
/// <returns>The decrypted string value</returns>
public string Decrypt(string input);
}
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
public sealed class JsonEncryptAttribute : Attribute
{
}
这里我假设需要_encryptionKey
盐将被封装在您的具体实现中IEncryptionService
.
接下来,定义以下合约解析器:
public class EncryptedPropertyContractResolver : DefaultContractResolver
{
IEncryptionService EncryptionService { get; }
public EncryptedPropertyContractResolver(IEncryptionService encryptionService) => this.EncryptionService = encryptionService ?? throw new ArgumentNullException(nameof(encryptionService));
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
var property = base.CreateProperty(member, memberSerialization);
return ApplyEncryption(property);
}
protected override JsonProperty CreatePropertyFromConstructorParameter(JsonProperty matchingMemberProperty, ParameterInfo parameterInfo)
{
var property = base.CreatePropertyFromConstructorParameter(matchingMemberProperty, parameterInfo);
return ApplyEncryption(property, matchingMemberProperty);
}
JsonProperty ApplyEncryption(JsonProperty property, JsonProperty matchingMemberProperty = null)
{
if ((matchingMemberProperty ?? property).AttributeProvider.GetAttributes(typeof(JsonEncryptAttribute), true).Any())
{
if (property.ItemConverter != null)
throw new NotImplementedException("property.ItemConverter");
property.Converter = new EncryptingJsonConverter(EncryptionService, property.Converter);
}
return property;
}
class EncryptingJsonConverter : JsonConverter
{
IEncryptionService EncryptionService { get; }
JsonConverter InnerConverter { get; }
public EncryptingJsonConverter(IEncryptionService encryptionService, JsonConverter innerConverter)
{
this.EncryptionService = encryptionService ?? throw new ArgumentNullException(nameof(encryptionService));
this.InnerConverter = innerConverter;
}
public override bool CanConvert(Type objectType) => throw new NotImplementedException(nameof(CanConvert));
object ReadInnerJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (InnerConverter?.CanRead == true)
return InnerConverter.ReadJson(reader, objectType, existingValue, serializer);
else
return serializer.Deserialize(reader, objectType);
}
public void WriteInnerJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (InnerConverter?.CanWrite == true)
InnerConverter.WriteJson(writer, value, serializer);
else
serializer.Serialize(writer, value);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return null;
else if (reader.TokenType != JsonToken.String)
throw new JsonSerializationException(string.Format("Unexpected token type {0}", reader.TokenType));
var encryptedString = (string)reader.Value;
var jsonString = EncryptionService.Decrypt(encryptedString);
using (var subReader = new JsonTextReader(new StringReader(jsonString)))
return ReadInnerJson(subReader.MoveToContentAndAssert(), objectType, existingValue, serializer);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
using var textWriter = new StringWriter();
using (var subWriter = new JsonTextWriter(textWriter))
WriteInnerJson(subWriter, value, serializer);
var encryptedString = EncryptionService.Encrypt(textWriter.ToString());
writer.WriteValue(encryptedString);
}
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
现在您可以申请[JsonEncrypt]
如问题中所示到您的模型,并按如下方式序列化和反序列化它的实例:
var resolver = new EncryptedPropertyContractResolver(_encryptionService);
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
};
var encryptedJson = JsonConvert.SerializeObject(model, Formatting.Indented, settings);
var model2 = JsonConvert.DeserializeObject<TopLevelModel>(encryptedJson, settings);
Notes:
-
适合哪些收藏JsonPropertyAttribute.ItemConverterType https://www.newtonsoft.com/json/help/html/P_Newtonsoft_Json_JsonPropertyAttribute_ItemConverterType.htm已设置未实施。
-
我尚未测试参数化构造函数参数的加密。
-
这个答案没有解决如何IEncryptionService
应实施。加密算法的安全实施超出了其范围。
演示小提琴here https://dotnetfiddle.net/sQZd0M.