我正在尝试通过派生自定义System.Web.Mvc.AuthorizeAttribute
并覆盖其某些方法来找到实现自定义HttpContextBase
的解决方案。
我正在尝试的每一种方法,我都面临着MVC 5的默认授权机制中的某些问题,这些问题阻止我正确地扩展它。
我已经在SO和许多专用资源上完成了关于这个领域的大量研究,但是我无法像现在那样为这个场景找到一个可靠的解决方案。
第一个限制:
我的授权逻辑需要其他数据,例如控制器和方法名称以及应用于它们的属性,而不是数据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
in
这个案子?System.Web.Mvc.AuthorizeAttribute
保护的方法?
必须在这里说我将使用我的自定义AuthorizeAttribute
作为全局过滤器,这是
如果回答这个问题,请完全告别缓存
问题是肯定的。 所以主要问题在这里:
处理此类自定义授权和正确缓存的可能方法是什么?
更新1(解决一些可能答案的其他信息):
MVC中没有保证每一个实例
AuthorizeAttribute
将提供单个请求。它可以重复使用
对于许多要求(见
here了解更多信息):
动作过滤器属性必须是不可变的,因为它们可能被缓存 通过部分管道并重复使用。取决于此属性的位置 在您的应用程序中声明,这会打开一个计时攻击,其中a 恶意网站访问者然后可以利用来授予自己访问权限 他希望的任何行动。
换句话说,AuthorizeAttribute
必须是不可变的和
不得在任何方法调用之间共享状态
而且在
AuthorizeAttribute
- 全局过滤方案,单个实例
AuthorizationContext
用于处理所有请求
如果您认为在OnAuthorization()
中为AuthorizeCore()
保存了AuthorizationContext
,那么您可以在后续的AuthorizeCore()
中获取相同的请求,那就错了。>
因此,您将根据其他请求中的OnAuthorization()
对当前请求作出授权决定。
AuthorizeAttribute
,则CacheValidateHandler()
之前从未调用当前请求(请AuthorizeCore()
ActionResult
从{{{{1}开始1}}下至AuthorizeCore()
)。OnAuthorization()
投放请求,则只会调用AuthorizationContext
,而不会调用AuthorizationContext
。
因此,在这种情况下,您无法保存OnAuthorization()
。 因此,在AuthorizeCore()
和{{1}}之间共享{{1}}不是必须的选择!
答案 0 :(得分: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
}
}
修改强>
因为亚历山大指出这不起作用。第二个选项可能是完全覆盖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;
}