需要使用 Castle Dynamic Proxy(或许使用 AutoMapper)拦截所有 LINQ-to-SQL 实体

发布于 2024-09-30 05:28:27 字数 3159 浏览 11 评论 0原文

我需要在存储在数据库中时对一组特定字段的值进行加密。 我正在使用 LINQ-to-SQL。

我的方法:在将实体中匹配属性的值写入数据库之前对其进行透明加密。

我已经用 Castle Dynamic Proxy 编写了一个拦截器,它将加密 setter 上的相关属性并在 getter 上解密。以下是我使用它的方式:

var secretEntity = <Get a SecretEntity from the DataContext>;
ProxyGenerator pg = new ProxyGenerator();
// (Typing from memory here, so excuse possible method errors, but you get the gist)
// Reassign to now reference the dynamic proxy.
secretEntity  = pg.CreateProxyWithTarget (secretEntity , new EncryptionInterceptor());
secretEntity.BigSecret = "Silentium"; // Does the encryption via the interceptor.
var decryptedSecret = secretEntity.BigSecret; // Does the decryption  via the interceptor.

现在这一切都很好,但我不想手动将每个 SecretEntity 实例包装在动态代理中。 所以我正在寻找一种自动化的方法,这样当我获得 SecretEntity 的实例时,它就已经包装在代理中了。

有没有办法通过某种方式连接到 LINQ-to-SQL DataContext 来做到这一点,以便它返回代理?

我使用的是 MVC,因此我使用视图模型来显示数据,并使用 AutoMapper 在实体和视图模型之间来回映射。所以我在想,如果 LINQ-to-SQL DataContext 方法不起作用,也许可以挂钩映射例程并将实体包装在代理中,然后再将它们映射到视图模型。 所以我很兴奋在使用AutoMapper的时候发现了一个叫BeforeMap的方法。 所以我尝试了

.BeforeMap ((se, sevm) => se = pg.CreateProxyWithTarget (se, new EncryptionInterceptor()));
// (Again, typing from memory).

但没有运气。我认为这是因为一旦 BeforeMap 方法完成运行,将“se”引用重新分配给代理就不会产生任何效果。

创建新的 SecretEntity 时,我可以使用 Ninject 自动执行代理包装过程,但 Ninject 不适用于我从 DataContext 返回的现有实体。

我使用 Castle Dynamic Proxy 的步行时间只有几个小时,而 AutoMapper 并不了解我。所以我希望有人能给我一个快速的指示去哪里寻找。

谢谢。

编辑

为了完整起见,我想为那些可能感兴趣的人添加拦截器的实现。由于不太了解 Castle Dynamic Proxy,我确信可能有更好的方法来处理拦截并检测它是 getter 还是 setter 等。 如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.DynamicProxy;
using TunedIn.Base.Model;
using TunedIn.Base.Ninject;
using TunedIn.Base.Extensions.StringExtensions;

namespace TunedIn.Base.Encryption
{
    public class PropertyEncryptionInterceptor : IInterceptor
    {
        public void Intercept (IInvocation invocation)
        {
            IPersonEncryptedFields p = invocation.InvocationTarget as IPersonEncryptedFields;
            if (p == null)
                throw new ApplicationException ("{0} expects the InvocationTarget of the dynamic proxy binding to implement {1}, but {2} does not.".FormatI (typeof (PropertyEncryptionInterceptor).FullName, typeof (IPersonEncryptedFields).FullName, invocation.InvocationTarget.GetType ().FullName));

            if (invocation.Method.Name.StartsWith ("set_"))
            {
                string val = (string)invocation.GetArgumentValue (0);
                val = Kernel.Get<IStringCrypto> ().Encrypt (val);
                invocation.SetArgumentValue (0, val);
                invocation.Proceed ();
            }
            else if (invocation.Method.Name.StartsWith ("get_"))
            {
                invocation.Proceed ();
                string ret = invocation.ReturnValue.ToString ();
                ret = Kernel.Get<IStringCrypto> ().Decrypt (ret);
                invocation.ReturnValue = ret;
            }
            else
                invocation.Proceed ();
        }
    }
}

I have a need to encrypt the value of a specific set of fields when stored in the DB.
I am using LINQ-to-SQL.

My approach: To transparently encrypt the value of the matching properties in the entity before it is written to the DB.

I have already written a interceptor with Castle Dynamic Proxy that will encrypt the relevant property on the setter and decrypt it on the getter. Here is how I use it:

var secretEntity = <Get a SecretEntity from the DataContext>;
ProxyGenerator pg = new ProxyGenerator();
// (Typing from memory here, so excuse possible method errors, but you get the gist)
// Reassign to now reference the dynamic proxy.
secretEntity  = pg.CreateProxyWithTarget (secretEntity , new EncryptionInterceptor());
secretEntity.BigSecret = "Silentium"; // Does the encryption via the interceptor.
var decryptedSecret = secretEntity.BigSecret; // Does the decryption  via the interceptor.

Now this all works great, but I don't want to have to manually wrap each SecretEntity instance in a dynamic proxy.
So I am looking for a way to automate this, such that when ever I get an instance of SecretEntity, it is already wrapped in the proxy.

Is there a way to do this by somehow hooking into the LINQ-to-SQL DataContext, so that it gives back proxies?

I am using MVC, so I use view models to display my data and I use AutoMapper to map back and forth between the entities and the view models. So I was thinking, if the LINQ-to-SQL DataContext approach does not work, perhaps it would be possible to hook into the mapping routine and wrap the entities in proxies before they are mapped to the view models.
So I was very excited to find a method called BeforeMap when using AutoMapper.
So I tried

.BeforeMap ((se, sevm) => se = pg.CreateProxyWithTarget (se, new EncryptionInterceptor()));
// (Again, typing from memory).

But no luck. I assume it is because reassigning the "se" reference to the proxy has no effect once the BeforeMap method has finished running.

When creating a new SecretEntity, I could automate the proxy wrapping process by using Ninject, but Ninject will not work on the existing entities I get back from the DataContext.

My walk with Castle Dynamic Proxy consists only of a couple of hours, and AutoMapper doesn't know me much better. So I'm hoping somebody can give me a quick pointer where to look.

Thank you.

Edit

For completeness sake, I thought I'd add the implementation of my interceptor for those who might be interested. Not knowing Castle Dynamic Proxy that well, I'm sure there might be a better way of handling the interception and detecting whether it is a getter or a setter, etc.
Here it follows:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Castle.DynamicProxy;
using TunedIn.Base.Model;
using TunedIn.Base.Ninject;
using TunedIn.Base.Extensions.StringExtensions;

namespace TunedIn.Base.Encryption
{
    public class PropertyEncryptionInterceptor : IInterceptor
    {
        public void Intercept (IInvocation invocation)
        {
            IPersonEncryptedFields p = invocation.InvocationTarget as IPersonEncryptedFields;
            if (p == null)
                throw new ApplicationException ("{0} expects the InvocationTarget of the dynamic proxy binding to implement {1}, but {2} does not.".FormatI (typeof (PropertyEncryptionInterceptor).FullName, typeof (IPersonEncryptedFields).FullName, invocation.InvocationTarget.GetType ().FullName));

            if (invocation.Method.Name.StartsWith ("set_"))
            {
                string val = (string)invocation.GetArgumentValue (0);
                val = Kernel.Get<IStringCrypto> ().Encrypt (val);
                invocation.SetArgumentValue (0, val);
                invocation.Proceed ();
            }
            else if (invocation.Method.Name.StartsWith ("get_"))
            {
                invocation.Proceed ();
                string ret = invocation.ReturnValue.ToString ();
                ret = Kernel.Get<IStringCrypto> ().Decrypt (ret);
                invocation.ReturnValue = ret;
            }
            else
                invocation.Proceed ();
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

月下凄凉 2024-10-07 05:28:27

也许我太简单了,但是仅仅向允许加密和解密的部分 SecretEntity 添加一个新属性不就足够了吗?您可以将 LINQ to SQL 生成的原始属性设置为内部属性:

public partial class SecretEntity
{
    public string BigSecret
    {
        get { return Decrypt(this.BigSecretInternal); }
        set { this.BigSecretInternal = Encrypt(value); }
    }
}

Perhaps I'm being too simplistic, but wouldn't it be sufficient to just add a new property to the partial SecretEntity that allows encrypting and decrypting? You can make the original property that is generated by LINQ to SQL internal:

public partial class SecretEntity
{
    public string BigSecret
    {
        get { return Decrypt(this.BigSecretInternal); }
        set { this.BigSecretInternal = Encrypt(value); }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文