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