检测是否手动调用了 AuthorizationAttribute

Detecting if AuthorizationAttribute manually called

我在遗留 MVC5 项目中有一个自定义 AuthorizeAttribute

public class AuthorizeWithLoggingAttribute : AuthorizeAttribute
{
     public override void OnAuthorization(AuthorizationContext filterContext)
     {
         if (!base.AuthorizeCore(httpContext)) {Log(FilterContext);}
     }
 }

我们在查看日志时注意到,除了使用 [AuthorizeWithLogging] 应用于控制器外,它还在代码的其他地方被显式调用,生成虚假日志:

var filters = new FilterInfo(FilterProviders.Providers.GetFilters(controllerContext, actionDescriptor));
foreach (var authFilter in filters.AuthorizationFilters)
{
    authFilter.OnAuthorization(authContext);
    if (authContext.Result != null) {return false;}
}

有没有办法告诉(通过 StackTrace 或其他方式)OnAuthorization 方法是被显式调用,还是从属性中调用?我目前拥有的最好的是 Environment.StackTrace.Contains("at System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters").

您可以选择的一条路线是使用 StackFrame。这会比你目前拥有的更干净一些。更多详情可在这找到: How can I find the method that called the current method

AuthorizeAttribute 有一个单一的责任:确定用户是否被授权。由于各种不同的原因,这可以在应用程序的多个地方使用。

由于未获得授权而采取的任何操作(例如返回 HTTP 401 响应)都会委托给类型为 ActionResult 的处理程序,该处理程序设置为 AuthorizationContext.Result 属性。例如,这里是 AuthorizeAttribute.HandleUnauthorizedRequest 的默认实现:

protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    // Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
    filterContext.Result = new HttpUnauthorizedResult();
}

如果您尝试在用户未经授权时进行审计,您应该将审计放入 ActionResult 处理程序,而不是自定义 AuthorizeAttribute。这确保仅在执行 ActionResult 时(即当前页面未授权时)才执行审核,而不是在所有情况下都检查授权。

public class AuthorizeWithLoggingAttribute : AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        filterContext.Result = new LoggingActionResult(new HttpUnauthorizedResult(), filterContext);
    }
}

public class LoggingActionResult : ActionResult
{
    private readonly ActionResult innerActionResult;
    private readonly AuthorizationContext filterContext;

    public LoggingActionResult(ActionResult innerActionResult, AuthorizationContext filterContext)
    {
        if (innerActionResult == null)
            throw new ArgumentNullException("innerActionResult");
        if (filterContext == null)
            throw new ArgumentNullException("filterContext");

        this.innerActionResult = innerActionResult;
        this.filterContext = filterContext;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        // Do logging (or apparently you want auditing) here
        Log(this.filterContext);

        innerActionResult.ExecuteResult(context);
    }
}

NOTE: I would name them AuthorizeWithAuditingAttribute and AuditingActionResult since you clearly want auditing, not logging in this case.