MVC 5:自定义AuthorizeAttribute和缓存

Ale*_*mov 10 .net c# asp.net asp.net-mvc caching

我试图找到一个实现自定义的解决方案,System.Web.Mvc.AuthorizeAttribute通过从它派生并覆盖它的一些方法.
我正在尝试的每种方法,我都面临着MVC 5的默认授权机制中的某些问题,这些问题阻止我正确扩展它.
我已经在SO和许多专用资源上完成了关于这个领域的大量研究,但是我无法像现在那样得到一个可靠的解决方案.

第一个限制:
我的授权逻辑需要额外的数据,如控制器和方法名称以及应用于它们的属性,而不是数据的有限部分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);
    ...
}
Run Code Online (Sandbox Code Playgroud)

这就是为什么我无法在AuthorizeCore()方法覆盖中实现我的授权逻辑,因为它只HttpContextBase作为参数和我需要做出授权决策AuthorizationContext.
这导致我将我的授权逻辑放到OnAuthorization()方法覆盖中,如上例所示.

但是在这里我们遇到了第二个限制:缓存系统调用
AuthorizeCore()方法来做出授权决策,是否应当使用缓存ActionResult或相应的控制器方法来提供当前请求,以创建新的ActionResult.
所以我们不能忘记AuthorizeCore()并且OnAuthorization()只使用它.

在这里,我们回到最初的观点:如果我们需要更多的数据,
如何根据缓存系统做出授权决策HttpContextBaseAuthorizationContext
随后有许多问题,如:

  • AuthorizeCore()在这种情况下,我们应该如何正确实施?
  • 我应该实现自己的缓存,以便为授权系统提供足够的数据吗?如果可以的话怎么做?
  • 或者我应该告诉我的自定义保护的所有控制器方法的缓存System.Web.Mvc.AuthorizeAttribute这里必须要说的是,我将使用我的自定义System.Web.Mvc.AuthorizeAttribute作为全局过滤器,如果这个问题的答案是肯定的,那么这就是缓存的完全再见.

所以这里的主要问题:
处理这种自定义授权和正确缓存的可能方法是什么?

更新1(解决一些可能答案的附加信息):

  1. MVC中没有保证,每个实例 AuthorizeAttribute都会提供单个请求.它可以重复用于许多请求(有关详细信息,请参阅 此处):

    操作过滤器属性必须是不可变的,因为它们可能被管道的某些部分缓存并重新使用.根据应用程序中声明此属性的位置,这会打开一个计时攻击,然后恶意网站访问者可以利用该攻击授予自己访问他希望的任何操作的权限.

    换句话说,AuthorizeAttribute 必须是不可变的,并且 不得在任何方法调用之间共享状态.
    此外,在 AuthorizeAttribute-as-global-filter场景中,单个实例AuthorizeAttribute用于为所有请求提供服务.
    如果你认为你保存AuthorizationContextOnAuthorization()一个请求中,那么你可以在后续AuthorizeCore()的同一个请求中得到它,你错了.
    因此,您将根据AuthorizationContext其他请求对当前请求作出授权决策.

  2. 如果a AuthorizeCore()由缓存层触发,OnAuthorization()则从未调用过当前请求(请参阅从下到的起始). 换句话说,如果要使用缓存提供请求,则只会调用而不是. 所以在这种情况下你无法保存.AuthorizeAttributeCacheValidateHandler()AuthorizeCore()
    ActionResultAuthorizeCore()OnAuthorization()
    AuthorizationContext

因此,共享AuthorizationContext之间OnAuthorization()AuthorizeCore()没有选择!

Mar*_*Ban 5

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

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
    }    
}
Run Code Online (Sandbox Code Playgroud)

编辑

因为亚历山大指出这不会起作用.第二个选项可能是完全覆盖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;

    }
Run Code Online (Sandbox Code Playgroud)

  • 这是不正确的.首先,MVC中没有保证,"AuthorizeAttribute"的每个实例都将提供单个请求.它可以重复用于许多请求(有关详细信息,请参阅[此处](http://stackoverflow.com/a/4890406/3345644)).换句话说,`AuthorizeAttribute`必须是不可变的,并且不在方法调用之间共享状态.此外,在`AuthorizeAttribute`-as-global-filter场景中,`AuthorizeAttribute`的单个实例用于服务所有请求. (2认同)
  • 其次,如果缓存层触发了`AuthorizeCore()`,那么OnAuthorization()`之前从未调用当前请求(请参阅[sources](https://aspnetwebstack.codeplex.com/SourceControl/latest#) src/System.Web.Mvc/AuthorizeAttribute.cs)从`CacheValidateHandler()`开始到`AuthorizeCore()`).换句话说,如果要使用缓存的`ActionResult`来提供请求,则只调用`AuthorizeCore()`而不是`OnAuthorization()`.所以在这种情况下你无法保存`AuthorizationContext`. (2认同)