我试图通过从中派生并重写它的一些方法来找到实现customSystem.Web.Mvc.AuthorizeAttribute的解决方案。
我正在尝试的每一种方法,都面临着MVC5默认授权机制中的某些问题,这些问题阻碍了我对其进行适当的扩展。
我对这个领域做了大量的研究,投入了大量的资源,但是我没有像现在这样的解决方案。
第一个限制:
我的授权逻辑需要额外的数据,比如控制器和方法名以及应用到它们的属性,而不是HttpContextBase能够提供的有限部分数据。
例子:

public override void OnAuthorization(AuthorizationContext filterContext)
{
    ...
    var actionDescriptor = filterContext.ActionDescriptor;
    var currentAction = actionDescriptor.ActionName;
    var currentController = actionDescriptor.ControllerDescriptor.ControllerName;

    var hasHttpPostAttribute = actionDescriptor.GetCustomAttributes(typeof(HttpPostAttribute), true).Any();
    var hasHttpGetAttribute = actionDescriptor.GetCustomAttributes(typeof(HttpGetAttribute), true).Any();

    var isAuthorized = securitySettingsProvider.IsAuthorized(
        currenPrincipal, currentAction, currentController, hasHttpPostAttribute, hasHttpGetAttribute);
    ...
}

这就是为什么我不能在AuthorizeCore()方法重写中实现我的授权逻辑,因为它只得到HttpContextBase作为参数,而我需要做出授权决策的是AuthorizationContext
这导致我将我的授权逻辑放到OnAuthorization()方法重写中,如上面的示例所示。
但这里我们来讨论第二个限制:
缓存系统调用AuthorizeCore()方法,以作出授权决策:当前请求是否应与缓存的ActionResult一起服务,或者应使用相应的控制器方法来创建新的ActionResult
所以我们不能忘记AuthorizeCore()而只使用OnAuthorization()
现在我们回到原点:
如何仅当我们需要来自HttpContextBase的更多数据时才基于AuthorizationContext对缓存系统进行授权决策?
接下来还有很多问题,比如:
我们应该如何正确地实现
这个案子?
我应该实现自己的缓存来让它提供
授权系统是否有足够的数据?以及如何做到这一点
如果是的话?
或者我应该跟所有控制器的缓存说再见
使用我的自定义AuthorizeCore()保护的方法?
这里必须说明的是,我将使用我的自定义System.Web.Mvc.AuthorizeAttribute作为全局过滤器,这是
如果这个问题的答案是
问题是肯定的。
所以这里的主要问题是:
处理这种自定义授权和适当缓存的可能方法是什么?
更新1(提供其他信息以解决一些可能的问题):
MVC中没有保证
System.Web.Mvc.AuthorizeAttribute将满足单个请求。可以重复使用
对于许多请求(请参见
here了解更多信息:
操作筛选器属性必须是不可变的,因为它们可能被缓存
通过部分管道再利用。取决于此属性的位置
在应用程序中声明,这将打开计时攻击,而
恶意网站访问者可以利用此漏洞授予自己访问
任何他想做的事。
换句话说,AuthorizeAttribute必须是不可变的,并且
不能在任何方法调用之间共享状态。
而且在
AuthorizeAttribute-作为全局筛选方案,一个
AuthorizeAttribute用于满足所有请求。
如果您认为将AuthorizeAttribute保存到AuthorizationContext中是为了一个请求,那么您就可以在随后的OnAuthorization()中获得相同的请求,这是错误的。
因此,您将根据另一个请求中的AuthorizeCore()对当前请求进行授权决策。
如果缓存层触发了AuthorizationContext,则AuthorizeCore()以前从未为当前请求调用过(请参阅OnAuthorization()中从AuthorizeAttribute开始到CacheValidateHandler()sources)。
换句话说,如果要使用缓存的AuthorizeCore()来服务请求,则只调用ActionResult,而不调用AuthorizeCore()
因此在这种情况下,您无法保存OnAuthorization()
因此,在AuthorizationContextAuthorizationContext之间共享OnAuthorization()不是选项!

最佳答案

onAuthorization方法在authorizeCore方法之前调用。因此,您可以保存当前上下文以供以后处理:

public class MyAttribute: AuthorizeAttribute
{
    # Warning - this code doesn't work - see comments

    private AuthorizationContext _currentContext;

    public override void OnAuthorization(AuthorizationContext filterContext)
    {
         _currentContext = filterContext;
         base.OnAuthorization(filterContext);
    }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
         // use _currentContext
    }
}

编辑
因为这不会像亚历山大指出的那样奏效。第二个选项可以是完全重写onauthorization方法:
        public override void OnAuthorization(AuthorizationContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }

            if (OutputCacheAttribute.IsChildActionCacheActive(filterContext))
            {
                throw new InvalidOperationException(MvcResources.AuthorizeAttribute_CannotUseWithinChildActionCache);
            }

            bool skipAuthorization = filterContext.ActionDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true)
                                     || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(AllowAnonymousAttribute), inherit: true);

            if (skipAuthorization)
            {
                return;
            }

            if (AuthorizeCore(filterContext.HttpContext))
            {
                HttpCachePolicyBase cachePolicy = filterContext.HttpContext.Response.Cache;
                cachePolicy.SetProxyMaxAge(new TimeSpan(0));

                var actionDescriptor = filterContext.ActionDescriptor;
                var currentAction = actionDescriptor.ActionName;
                var currentController = actionDescriptor.ControllerDescriptor.ControllerName;

                var hasHttpPostAttribute = actionDescriptor.GetCustomAttributes(typeof(HttpPostAttribute), true).Any();
                var hasHttpGetAttribute = actionDescriptor.GetCustomAttributes(typeof(HttpGetAttribute), true).Any();
                // fill the data parameter which is null by default
                cachePolicy.AddValidationCallback(CacheValidateHandler, new { actionDescriptor : actionDescriptor, currentAction: currentAction, currentController: currentController, hasHttpPostAttribute : hasHttpPostAttribute, hasHttpGetAttribute: hasHttpGetAttribute  });
            }
            else
            {
                HandleUnauthorizedRequest(filterContext);
            }
        }

    private void CacheValidateHandler(HttpContext context, object data, ref HttpValidationStatus validationStatus)
    {
        if (httpContext == null)
        {
            throw new ArgumentNullException("httpContext");
        }
        // the data will contain AuthorizationContext attributes
        bool isAuthorized = myAuthorizationLogic(httpContext, data);
        return (isAuthorized) ? HttpValidationStatus.Valid : httpValidationStatus.IgnoreThisRequest;

    }

08-26 18:24
查看更多