Пользовательский IAuthorizationFilter, внедренный с Ninject, останавливает выполнение другого ActionFilterAttribute - PullRequest
0 голосов
/ 23 февраля 2012

В проекте, над которым я работаю, есть два пользовательских класса ActionFilterAttribute, которые внедряются с помощью нинъектов 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);

Сам по себе, это тоже отлично работает.

Я только что обнаружил, что если я применю тег Authorize к действию, два пользовательских класса 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; }
}

Буду признателен за любую помощь в решении этой проблемы.

Edit:

Это один из других фильтров:

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);
    }
}

Если объект домена не может быть гидратирован, он регистрирует критическую ошибку. Этот фильтр проверяет наличие таких ошибок, если они возникают, он направляет пользователя на страницу ошибок.

Решено:

Оказывается, Дарин был прав! Но проблема была скрыта моей конфигурацией моих фильтров. Во-первых, у меня было [Authorize] для элементов навигации, а во-вторых, я связывал CriticalErrorAttribute с каждым действием. Таким образом, каждый раз, когда создавалось меню (welcome, left, nav, sub) - этот фильтр срабатывал. В какой-то момент во время этой цепочки вызовов фильтра результаты применялись к filterContext.Result - более поздние результаты скрывали более ранний (правильный) результат.

Чтобы преодолеть эту проблему, я настроил строку конфигурации BindFilter для CriticalError. Атрибут для этого:

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

Теперь все отлично работает!

1 Ответ

4 голосов
/ 23 февраля 2012

Здесь:

filterContext.Result = ...

вы назначаете результат. А согласно документации :

Вы можете отменить выполнение фильтра в OnActionExecuting и Методы OnResultExecuting, задав для свойства Result ненулевое значение значение. Все ожидающие фильтры OnActionExecuted и OnActionExecuting будут не будет вызван и вызывающий не будет вызывать OnActionExecuted метод для отмененного фильтра или для ожидающих фильтров. Будет запущен фильтр OnActionExecuted для ранее запущенных фильтров. Все будут запущены фильтры OnResultExecuting и OnResultExecuted.

...