使用Ninject注入的自定义IAuthorizationFilter会停止执行其他ActionFilterAttrib

本文关键字:执行 其他 ActionFilterAttrib IAuthorizationFilter Ninject 注入 自定义 使用 | 更新日期: 2023-09-27 18:25:37

在我正在处理的项目中,有两个自定义ActionFilterAttribute类,它们是使用ninjects BindFilter注入的:

        kernel.BindFilter<LogErrorsAttribute>(FilterScope.Last, 0);
        kernel.BindFilter<CriticalErrorAttribute>(FilterScope.Last, 1);

这些一直运行良好。

我创建了一个自定义的IAuthorizationFilter过滤器,它也使用BindFilter:注入

        kernel.BindFilter<AuthorizationFilter>(FilterScope.Action, null).WhenActionMethodHas<Authorise>().WithPropertyValueFromActionAttribute<Authorise>("Roles", n => n.Roles).WithPropertyValueFromActionAttribute<Authorise>("Years", n => n.Years);

就其本身而言,这也很好。

我刚刚发现,如果我将Authorise标记应用于一个操作,那么两个自定义ActionFilterAttribute类将不再被调用。

我不明白为什么会发生这种事。我的自定义IAuthorizationFilter如下所示:

public class AuthorizationFilter : IAuthorizationFilter
{
    private readonly string[] RolesHaveAccessToApplication;
    public AuthorizationFilter()
    {
        //put roles which should allow user to see application, hardcoded for now, but later
        //this can be generated from the database
        var configRoles = ConfigurationManager.AppSettings["ApplicationRoles"];
        if(string.IsNullOrEmpty(configRoles))
            throw new Exception("The ApplicationRoles value has not been defined in the web.config file.");
        RolesHaveAccessToApplication = configRoles.Split(',');
    }
    [Inject]
    public IUserServices userService { get; set; }
    public string Roles { get; set; }
    public string Years { get; set; }

    protected bool AuthorizeCore(HttpContextBase httpContext)
    {
        if (!httpContext.Request.IsAuthenticated)
            return false;

        if(!Roles.HasContent() && !Years.HasContent())
        {
            return RolesHaveAccessToApplication.Any(role => RolesHaveAccessToApplication.Any(n => n == role));
        }
        var AuthenticatedUserRoles = System.Web.Security.Roles.GetRolesForUser();
        bool isAuthorised = false;
        //first, lets check against to see if the user has any roles related to the application
        isAuthorised = RolesHaveAccessToApplication.Any(role => AuthenticatedUserRoles.Any(n => n == role));
        //if they don't, we throw them to access denied page
        if (!isAuthorised)
            return false;
        #region CheckRoles
        if (!string.IsNullOrEmpty(Roles) && AuthenticatedUserRoles.HasContent())
        {
            var authRoles = Roles.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            isAuthorised = authRoles.Any(role => AuthenticatedUserRoles.Any(n => n == role));
        }
        #endregion
        #region CheckYears
        if (!string.IsNullOrEmpty(Years) && AuthenticatedUserRoles.HasContent())
        {

            if (AuthenticatedUserRoles.Any(n => n == "Student"))
            {
                var yearRoles = Years.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                var user = userService.FetchUser(httpContext.User.Identity.Name);
                if (user != null)
                {
                    isAuthorised = yearRoles.Any(n => n == user.Year);
                }
            }
        }
        #endregion
        return isAuthorised;
    }
    public void OnAuthorization(AuthorizationContext filterContext)
    {
        if(filterContext == null)
            throw new Exception("filtercontext is null");
        if (!filterContext.HttpContext.Request.IsAuthenticated)
            HandleUnauthorizedRequest(filterContext);
            if (AuthorizeCore(filterContext.HttpContext))
                SetCachePolicy(filterContext);
            else
                HandleUnauthorizedRequest(filterContext);

    }
    protected void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
            filterContext.Result = new RedirectResult(new UrlHelper(filterContext.RequestContext).Action("Denied", "Home", new {Area = ""}));// new ViewResult { ViewName = "~/Home/Denied", View = new RazorView("Home") //ViewData = viewData }; 
        else
            filterContext.Result = new HttpUnauthorizedResult();
    }
    protected void SetCachePolicy(AuthorizationContext filterContext)
    {
           ..snip..
    } 
}
//Used as a filter for actions, and ninject is configured to bind AuthorizationFilter to this
public class Authorise : ActionFilterAttribute
{
    public string Roles { get; set; }
    public string Years { get; set; }
}

如有任何帮助,我们将不胜感激。

编辑:

这是其他过滤器之一:

public class CriticalErrorAttribute : ActionFilterAttribute
{
    [Inject]
    public IErrorServices ErrorService { private get; set; }
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {            //if the request is an ajax request, we don't want a redirect to happen
        //the controller dealing with the ajax request can fetch the critical
        //errors and pass them back to the user for display
        if (!filterContext.HttpContext.Request.IsAjaxRequest())
        {
            var criticalErrors = ErrorService.FetchCriticalErrors();
            if (criticalErrors.HasContent())
            {
                var helper = new UrlHelper(filterContext.RequestContext);
                var url = helper.Action("Error", "Home", new { area = "" });
                filterContext.Controller.TempData["CriticalErrorList"] = criticalErrors;
                filterContext.Result = new RedirectResult(url);
            }
        }
        base.OnActionExecuted(filterContext);
    }
}

如果域对象不能水合,它将记录一个严重错误。此筛选器检查此类错误,如果出现错误,则会将用户引导到错误页面。

已解决:

原来Darin是对的!但我的过滤器配置隐藏了这个问题。首先,我在导航项上有[Authorise],其次,我将CriticalErrorAttribute绑定到每个操作。因此,每次构建菜单(欢迎、左侧、导航、子菜单)时,这个过滤器都会启动。在这个筛选器调用链的某个时刻,结果被应用到filterContext.Result-后面的结果隐藏了前面的(正确的)结果。

为了克服这个问题,我将CriticalErrorAttribute的BindFilter配置行调整为:

kernel.BindFilter<CriticalErrorAttribute>(FilterScope.Last, 0).When( (context, ad) =>
context.RouteData.DataTokens["action"] != null && context.RouteData.DataTokens["action"] !=
"Error" && context.RouteData.DataTokens["controller"] != "Navigation");

现在一切都很顺利!

使用Ninject注入的自定义IAuthorizationFilter会停止执行其他ActionFilterAttrib

此处:

filterContext.Result = ...

您正在分配一个结果。根据文件:

您可以在OnActionExecuting和通过将Result属性设置为非null来执行OnResultExecuting方法价值任何挂起的OnActionExecuted和OnActionExecuting筛选器都将不会被调用,调用程序也不会调用OnActionExecuted方法,用于已取消的筛选器或挂起的筛选器。这个将运行以前运行的筛选器的OnActionExecuted筛选器。所有将运行OnResultExecuting和OnResultExecuted筛选器。

相关文章: