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

时间:2013-07-03 15:11:35

标签: c# nhibernate serialization asp.net-web-api sensitive-data

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

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

    [Sensitive]
    public string SomeOtherProperty { get; set; }
}

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

我确实认为可以处理的另一种方式是在ORM级别,但在网上找不到好的例子。

非常感谢!

编辑:我在这里找到了另一个类似的问题: Contextual serialization from WebApi endpoint based on permissions 但没有解决方案。另外,我不喜欢通过属性在模型中设置基于角色的访问的想法。我认为应该在Web应用程序中处理。

3 个答案:

答案 0 :(得分:8)

正如问题所述,我提出了Contextual serialization from WebApi endpoint based on permissions这个问题,我自己answered。我最初看到使用的是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]);
            }
        }   
    }
}

答案 1 :(得分:0)

我提出的一个建议的解决方案是让我的ApiController从BaseApiController继承,它覆盖initalize函数,根据用户的角色设置适当的格式化程序:

protected override void Initialize(System.Web.Http.Controllers.HttpControllerContext controllerContext)
{
    base.Initialize(controllerContext);
    // If the user is in a sensitive-data access role
    controllerContext.Configuration.Formatters.Add(/*My Formatter*/);
    // Otherwise use the default ones added in global app_start that defaults to remove sensitive data
}

答案 2 :(得分:0)

您可以使用Json.Net中内置的条件序列化。 这是一篇描述它的文章。

http://blog.mariusschulz.com/2013/04/15/conditionally-serializing-fields-and-properties-with-jsonnet

也许'ShouldSerialize ...'方法可以检查当前的主体,从中可以检查它的角色?