将与MVC无关的属性应用于MVC操作

本文关键字:MVC 应用于 操作 属性 将与 | 更新日期: 2023-09-27 18:22:20

我们的应用程序具有PermissionAttribute的概念。这个属性是在我们的应用程序的底层中定义的,我们的命令和查询都是用这个属性装饰的。由于这个属性是在底层中定义的,我们不能(也不想)让它继承FilterAttribute或在它上面实现System.Web.Mvc.IActionFilter

尽管如此,我们还是希望将此属性应用于控制器操作,如下所示:

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

基于此属性,应该应用适当的安全检查。我一直在浏览MVC代码库,以找到合适的挂钩来自定义MVC行为,从而允许基于此自定义属性添加这些安全检查。我想创建一个自定义ControllerActionInvoker,它从GetControllerDescriptor方法返回一个自定义的ReflectedControllerDescriptor,该方法将返回基于PermissionAttribute的存在而创建的FilterAttribute,但这感觉工作量很大,我不确定这是正确的路径。

什么是定制MVC管道的有效且愉快的方式,以便我们能够处理这个与MVC无关的属性?

将与MVC无关的属性应用于MVC操作

我会这样做。首先创建自己的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());

我最终做了以下事情:

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();
        }
    }
}