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

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

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

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

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

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

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

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

Ответ 1

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

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

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

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

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

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

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

Ответ 2

Лучше расширить класс 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);
    }
}

Ответ 3

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

Создан класс, который расширяет 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 заботится о вложении репозитория. Надеюсь, это поможет кому-то.