Пользовательская авторизация MVC 3 и Ninject IoC

У меня есть пользовательский класс авторизации, который наследуется от FilterAttribute и реализует IAuthorizationFilter. Я использую последнюю версию Ninject с поддержкой MVC 3.

Проблема, с которой я столкнулся, заключается в том, что я использую инъекцию конструктора для внедрения хранилища. Но к тому времени, когда вызывается OnAuthorization, хранилище становится пустым. Вот код...

public class MyAuthorizeAttribute : FilterAttribute, IAuthorizationFilter
    {
        private readonly IMyRepo _MyRepo;

        public MyAuthorizeAttribute() { }
        public MyAuthorizeAttribute(IMyRepo myRepo)
        {
            _MyRepo= myRepo; //this gets initialized
        }


        public void OnAuthorization(AuthorizationContext filterContext)
        {
            _MyRepo.DoStuff(); //<< Null, wtf

        }
    }

Привязка фильтра:

Bind<IMyRepo>().To<MyRepo>().InRequestScope();


this.BindFilter<MyAuthorizeAttribute >(System.Web.Mvc.FilterScope.Controller, null).WhenControllerHas<MyAuthorizeAttribute >();

Обновление: я заметил, что этот фильтр находится на уровне контроллера. У меня есть другие фильтры в области действия, которые, кажется, работают должным образом... может ли это быть причиной?

Обновление 2: я подтвердил, что если я изменю область действия фильтра на действие, то в репозитории будет доступно OnAuthorization (не ноль).

Это работает ниже, однако мне нужно на уровне контроллера, а не действия.

this.BindFilter<MyAuthorizeAttribute >(System.Web.Mvc.FilterScope.Action, null).WhenActionMethodHas<MyAuthorizeAttribute >();

3 ответа

Решение

Атрибуты не поддерживают внедрение конструктора, так как они создаются.NET Framework и не контролируются Ninject. Если вы действительно хотите использовать FilterAttribute (который я не рекомендую), вам придется использовать инъекцию свойств.

Вместо этого продолжайте то, что вы только начали. Вам нужен фильтр, реализующий IAuthorizationFilter (не производный от FilterAttribute, просто удалите его из приведенного выше кода) и дополнительно обычный атрибут для маркировки контроллеров / действий.

Затем измените привязку:

this.BindFilter<MyAuthorizeFilter>(FilterScope.Controller, 0).WhenControllerHas<MyAuthorizeAttribute>();

Смотрите: https://github.com/ninject/ninject.web.mvc/wiki/MVC3

Проблема текущей реализации заключается в том, что она однажды обнаруживается как атрибут фильтра, а затем добавляется как обычный фильтр. В одном случае для этих репозиториев будет введен репо, а для другого репо является нулевым.

ПРИМЕЧАНИЕ: вы можете наследовать от существующего FilterAttribute, если это упрощает вашу реализацию. Но не используйте его как атрибут в этом случае, а используйте его как обычный фильтр.

Лучше расширить класс AuthorizeAttribute, чтобы авторизация корректно работала с кэшированными запросами. Вам также нужно будет использовать Ninject.Web.Mvc

Вам нужно будет использовать инъекцию Ninject, чтобы использовать ваш репозиторий. Внедрение в конструктор не будет работать с Атрибутами.

public class MyAuthorizeAttribute : AuthorizeAttribute
{
    [Inject]
    public IMyRepo MyRepo { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        return base.AuthorizeCore(httpContext);
    }
}

Просто подумал, что я добавлю свое решение здесь, как кажется, работает нормально.

Создан класс, который расширяет AuthorizeAttribute и принимает интерфейс репозитория в конструкторе.

Этот класс затем переопределяет функцию AuthorizeCore:

public class MyRoleAttribute : AuthorizeAttribute
{
    private ICRepository repository;

    public MyRoleAttribute(ICRepository Repo)
    {
        repository = Repo;
    }

protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        //Check if user authenticated
        if (!httpContext.Request.IsAuthenticated)
            return false;

         //Can access items in the query string if needed
         var id = (httpContext.Request.RequestContext.RouteData.Values["id"] as string)
         ??(httpContext.Request["id"] as string);

          //Can access repository that has been injected
          if (repository.IsGroupCreator(.....))
            {

                return true;

            }
            else
            {

                return false;

            }
    }
}

Затем для внедрения в репозиторий я добавил следующий код в файл mvc NinjectWebCommon.cs:

kernel.BindFilter<MyRoleAttribute>(FilterScope.Action, 0).When(
(controllerContext, actionDescriptor) => actionDescriptor.ActionName == "MyAction");

Это тогда позволяет мне контролировать, какие действия мне нужны для атрибута, а ninject заботится о внедрении репозитория. Надеюсь, это кому-нибудь поможет.

Другие вопросы по тегам