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