【发布时间】:2018-07-08 19:32:57
【问题描述】:
我正在尝试在 c# 中编写一个 MongoDb 序列化程序,它允许我通过 [Encrypt()] 属性装饰属性,然后在运行时它允许我生成一个名为 PropertyName_Encrypted 将包含加密值。
在反序列化时,将在父属性中设置加密属性值,以便该属性的默认 GET 始终返回加密值。然后,用户将在对象上调用可选的 Decrypt() 方法来获取解密后的值。
在此过程中,我遇到了一些有趣的挑战:
在序列化当前元素时,如何向文档添加其他属性?如何获取当前元素的名称?
有没有办法从文档/对象中读取特定属性?例如假设我想传递一个对称加密密钥并读取它以在序列化当前元素时加密数据?有什么办法可以做到吗?
以下是我到目前为止所做的事情:
-
我已经建立了一个加密属性如下:
[AttributeUsage(AttributeTargets.Property)] public class EncryptAttribute : Attribute { private readonly EncryptedFieldType _fieldType; private readonly bool _tokenizeDisplay; private readonly string _encryptedFieldName; /// <summary> /// /// </summary> /// <param name="fieldType">The field type to encrypt. Useful if display needs to show some formatting. If no formatting is necessary, simply set to "Other".</param> /// <param name="tokenizeDisplay">If set to true, will persist the tokenized value in the original field for display purposes.</param> /// <param name="encryptedFieldName">Optional. If set, will save the encrypted value in the field name specified. By default all encrypted field values are stored in the corresponding _Encrypted field name. So EmailAddress field if encrypted, would have value under EmailAddress_Encrypted.</param> public EncryptAttribute(EncryptedFieldType fieldType, bool tokenizeDisplay, string encryptedFieldName = "") { _fieldType = fieldType; _tokenizeDisplay = tokenizeDisplay; _encryptedFieldName = encryptedFieldName; } } -
我在启动时读取了此属性,并将加密序列化程序添加到使用此属性修饰的属性中。这样做的代码是这样的:
var assemblies = AppDomain.CurrentDomain.GetAssemblies() .Where(x => x.FullName.StartsWith("MongoCustomSerializer")) .ToList(); var mapper = new Mapper(); foreach (var assembly in assemblies) { mapper.Map(assembly); } -
映射器只需检查文档中哪些属性具有 Encrypt 属性即可添加序列化程序:
public sealed class Mapper { public void Map(Assembly assembly) { var encryptableTypes = assembly.GetTypes().Where(p => typeof(IEncryptable).IsAssignableFrom(p) && p.IsClass && !p.IsInterface && !p.IsValueType && !p.IsAbstract).ToList(); if (encryptableTypes.Any()) { foreach (var encryptableType in encryptableTypes) { Map(encryptableType); } } } private void Map(Type documentType) { var properties = documentType.GetProperties(BindingFlags.Public | BindingFlags.Instance); if (properties.Length <= 0) { return; } foreach (var property in properties) { RegisterEncrpytionSerializer(property, typeof(EncryptAttribute), documentType); } } private void RegisterEncrpytionSerializer(PropertyInfo property, Type encryptAttributeType, Type documentType) { var encryptAttributes = property.GetCustomAttributes(encryptAttributeType, false).ToList(); if (!encryptAttributes.Any()) return; var memberMap = BsonClassMap.LookupClassMap(documentType).GetMemberMap(property.Name); memberMap?.SetSerializer(new EncryptionSerializer()); }}
在我的单元测试中,我收到一条错误消息,指出 Bson 类映射已被冻结。即使我想办法绕过它,这个 EncryptionSerializer 类将如何工作到我可以编写附加属性的地方?
很想看看是否有人可以提供帮助!干杯!
更新 1 - 我能够解决 FREEZE 错误。 LookupClassMap 似乎冻结了成员和类映射信息。
This change from the link 允许我处理这个问题:
private void RegisterEncrpytionSerializer(PropertyInfo property, Type encryptAttributeType, Type documentType)
{
var encryptAttributes = property.GetCustomAttributes(encryptAttributeType, false).ToList();
if (!encryptAttributes.Any()) return;
var classMapDefinition = typeof(BsonClassMap<>);
var classMapType = classMapDefinition.MakeGenericType(documentType);
var classMap = (BsonClassMap)Activator.CreateInstance(classMapType);
classMap.AutoMap();
var memberMap = classMap.GetMemberMap(property.Name);
memberMap?.SetSerializer(new KeyVaultEncryptionSerializer(memberMap.ElementName));
}
【问题讨论】:
-
您能否分享一下您的 EncryptionSerializer 目前的样子?
-
您好,我的加密序列化器只是实现了 IBsonSerializer。我永远无法完成它,因为我需要访问另一个字段来获取密钥信息。让我尽可能地重新写一遍,看看我是否可以发布一个 Gist。
-
你好@marcofo88,这里是要点:gist.github.com/amarwadi/72e40954095f902d55b4ab7b9f61f183 你可以看到我的计划是提取 BSON 文档中已经可用的另一个属性,然后用它来加密当前值。问题是,无法从 BSON 文档中获取任何其他属性。
-
仅供参考,有 Ruby Gems 已经这样做了。我想构建一个任何人都可以使用的可插拔组件,只要他们设置了主密钥和内容加密密钥。 compose.com/articles/…
标签: c# mongodb mongodb-.net-driver