我试图通过从中派生并重写它的一些方法来找到实现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()
。因此,在
AuthorizationContext
和AuthorizationContext
之间共享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;
}