将非MVC相关属性应用于MVC操作

时间:2014-09-17 10:07:08

标签: c# .net asp.net-mvc security cross-cutting-concerns

我们的应用程序具有PermissionAttribute的概念。此属性在我们的应用程序的基础层中定义,我们的命令和查询使用该属性进行修饰。由于此属性是在基础层中定义的,因此我们不能(也不希望)让它从FilterAttribute继承或在其上实现System.Web.Mvc.IActionFilter

我们仍然希望将此属性应用于控制器操作,如下所示:

[Permission(Permissions.Administration.ManageUsers)]
public ActionResult Index()
{
    return this.View();
}

基于此属性,应该应用适当的安全检查。我一直在浏览MVC代码库以找到适当的钩子来定制MVC行为,以允许基于此自定义属性添加这些安全检查。我虽然创建了一个自定义ControllerActionInvoker,它从ReflectedControllerDescriptor方法返回了一个自定义GetControllerDescriptor,该方法将返回FilterAttribute,该PermissionAttribute将根据{{1}的存在而创建但感觉就像很多工作一样,而且我不确定这是走路的正确道路。

定制MVC管道的有效和愉快的方法是什么,以便我们可以处理这个非MVC相关的属性?

2 个答案:

答案 0 :(得分:2)

我会这样做。首先像这样创建自己的AuthorizeAttribtues实现:

public class PermissionAuthoriseAttribute : AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        //Leaving the implementation of this to you, but you check if your
        //PermissionAttribute is assigned and call it's methods.
        if(...)
            return true;

        //You may want to check this first, depending on your requirements
        return base.AuthorizeCore(httpContext);
    }
}

然后将此行添加到FilterConfig.cs文件中,将其应用于整个项目:

filters.Add(new PermissionAuthoriseAttribute());

答案 1 :(得分:2)

我最终做了以下事情:

public class FilterConfig
{
    public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
        filters.Add(new PermissionAuthorizationFilter(
            () => Global.Container.GetInstance<IUserPermissionChecker>()), 0);
        filters.Add(new HandleErrorAttribute());
    }
}

public sealed class PermissionAuthorizationFilter : IAuthorizationFilter
{
    private readonly Func<IUserPermissionChecker> userPermissionCheckerFactory;

    public PermissionAuthorizationFilter(
        Func<IUserPermissionChecker> userPermissionCheckerFactory) {
        this.userPermissionCheckerFactory = userPermissionCheckerFactory;
    }

    public void OnAuthorization(AuthorizationContext filterContext) {
        var attribute = filterContext.ActionDescriptor
            .GetCustomAttributes(typeof(PermissionAttribute), true)
            .OfType<PermissionAttribute>()
            .SingleOrDefault();

        if (attribute != null) {
            VerifyPermission(filterContext, attribute.PermissionId);
        }
    }

    private static void VerifyPermission(AuthorizationContext filterContext,
        Guid permissionId) {
        var permissionChecker = userPermissionCheckerFactory.Invoke();

        if (!permissionChecker.HasPermission(permissionId))
        {
            filterContext.Result = new HttpUnauthorizedResult();
        }
    }
}