基于用户角色的ASP.NET WebAPI条件序列化

Zac*_*ach 8 c# nhibernate serialization sensitive-data asp.net-web-api

我有一个ORM(NHibernate)映射到POCO,它将在ApiControllers中返回.我意识到JSON.NET允许我在我的模型上放置条件序列化方法(ShouldSerialize*); 然而,这些模型及其方法对环境一无所知,也不应该知道.我想要做的是根据用户登录我的网站时的角色有条件地序列化模型或其一个或多个属性.我可以从概念上理解如何做到这一点,但我在一个部分迷失了.这是一个示例模型:

public class SomeModel
{
    public string SomeProperty { get; set; }

    [Sensitive]
    public string SomeOtherProperty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我希望能够在属性上添加一个属性,将其标记为"敏感".然后在我的WebApi中将它序列化为输出时,我希望它检查该属性的模型,如果存在,则应该检查用户的角色.如果用户处于指定角色,则序列化程序应该序列化该属性,否则它将掩盖它或者根本不对其进行序列化.那么我是否必须编写自己的自定义格式化程序来处理这个问题,或者有没有办法挂钩内置的格式化程序来执行此检查?或者我的想法太局限了,还有另一种方法可以解决这个问题吗?

我确实认为可以处理的其他方式是在ORM级别,但无法在线找到好的示例.

非常感激!

编辑:我在这里找到了另一个类似的问题: 基于权限的WebApi端点的上下文序列化, 但没有解决方案.另外,我不喜欢通过属性在模型中设置基于角色的访问的想法.我认为应该在Web应用程序中处理.

Bro*_*ski 8

正如问题中提到的,我提出了一个问题" 基于权限的WebApi端点的上下文序列化 ",我已经回答了这个问题.我最初看的是使用a MediaFormatter但是我相信这会限制你可以返回什么样的响应.如果要返回JSON和XML,则需要实现两个格式化程序.要只需要在一个地方实现过滤器,您需要在堆栈中向上移动到DelegatingHandler.

在我的实现中,我想查找客户端可以访问哪些字段,并从客户端不应该看到的响应中删除任何字段.这与您想要做的非常相似.

在您的senario中,您需要反映对象并选择包含您的属性的任何字段并将这些值设置为null.如果返回JSON或XML,那么如果值为null,则响应中不包含属性,因此您甚至不会泄漏属性名称.

下面是一个示例实现,DelegatingHandler它使用反射来过滤实现该Sensitive属性的响应对象内容的属性.它假定对象层次结构是平面的,因此如果您有嵌套对象,则需要导航对象图并对层次结构中的每个对象执行相同操作.

public class ResponseDataFilterHandler : DelegatingHandler
{
    protected override System.Threading.Tasks.Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken)
            .ContinueWith(task =>
            {
                var response = task.Result;

                //Manipulate content here
                var content = response.Content as ObjectContent;
                if (content != null && content.Value != null)
                {
                    FilterFields(content.Value);
                }

                return response;
            });
    }

    private void FilterFields(object objectToFilter)
    {
        var properties = objectToFilter
                             .GetType()
                             .GetProperties(
                                 BindingFlags.IgnoreCase |
                                 BindingFlags.GetProperty |
                                 BindingFlags.Instance |
                                 BindingFlags.Public);

        foreach (var propertyInfo in properties)
        {
            if (propertyInfo.GetCustomAttributes(typeof(SensitiveAttribute), true).Any())
            {
                propertyInfo.SetValue(objectToFilter, null, new object[0]);
            }
        }   
    }
}
Run Code Online (Sandbox Code Playgroud)