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(解决一些可能答案的附加信息):
MVC中没有保证,每个实例
AuthorizeAttribute都会提供单个请求.它可以重复用于许多请求(有关详细信息,请参阅
此处):
操作过滤器属性必须是不可变的,因为它们可能被管道的某些部分缓存并重新使用.根据应用程序中声明此属性的位置,这会打开一个计时攻击,然后恶意网站访问者可以利用该攻击授予自己访问他希望的任何操作的权限.
换句话说,AuthorizeAttribute 必须是不可变的,并且
不得在任何方法调用之间共享状态.
此外,在
AuthorizeAttribute-as-global-filter场景中,单个实例AuthorizeAttribute用于为所有请求提供服务.
如果你认为你保存AuthorizationContext在OnAuthorization()一个请求中,那么你可以在后续AuthorizeCore()的同一个请求中得到它,你错了.
因此,您将根据AuthorizationContext其他请求对当前请求作出授权决策.
AuthorizeCore()由缓存层触发,OnAuthorization()则从未调用过当前请求(请参阅从下到的起始源).
换句话说,如果要使用缓存提供请求,则只会调用而不是.
所以在这种情况下你无法保存.AuthorizeAttributeCacheValidateHandler()AuthorizeCore()ActionResultAuthorizeCore()OnAuthorization()AuthorizationContext因此,共享AuthorizationContext之间OnAuthorization()并AuthorizeCore()没有选择!
在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)