我在遗留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(“在System.Web.Mvc.ControllerActionInvoker.InvokeAuthorizationFilters”).
解决方法
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
andAuditingActionResult
since you clearly want auditing,not logging in this case.