Внедрение зависимостей в AuthorizeAttribute в библиотеке классов - PullRequest
7 голосов
/ 06 января 2012

У меня возникла интересная проблема дизайна с библиотекой классов, которую я пишу.У меня есть пользовательская реализация AuthorizeAttribute, которую я хочу, чтобы клиенты могли использовать следующим образом:

[Protected("permission_name")] 

В приведенном выше коде PermissionAttribute наследуется от AuthorizeAttribute и использует локальное значение по умолчанию (DefaultContext, созданный с использованием HttpContext).

За кулисами атрибут использует SecurityService для проверки пользователей, ролей и разрешений (сам SecurityService использует предоставляемую клиентом службу персистентности, которую они могут подключить в корне композиции своего приложения).

Так что для работы моих атрибутов нужна ссылка на SecurityService.Поскольку конструкторы атрибутов могут иметь только константы времени компиляции, я не могу использовать внедрение конструктора.

Я не хочу заставлять своих клиентов использовать инфраструктуру DI - они должны иметь возможность обнаружить и подключите необходимые зависимости в их корне композиции без использования библиотеки IoC, если они того пожелают.

Вот мои варианты:

  • Есть библиотекаиспользовать одноэлементную SecurityService.
  • Использовать внедрение свойства, которое бы работало, но
    1. это сделало бы зависимость необязательной, чего нет, и
    2. Я не знаю, гдеЯ могу выполнить внедрение свойства в приложении MVC для атрибута authorize.

Возможное решение для 2. выше состоит в том, чтобы установить экземпляр SecurityService в качестве статического свойства наАтрибут при запуске приложения и используйте пункт guard для предотвращения его установки более одного раза, например:

class ProtectedAttribute : ...
{
    private static ISecurityService _SecurityService ;
    public static ISecurityService SecurityService
    {
        get 
        {
            return _SecurityService ;
        }
        set 
        {
            if (_SecurityService != null)
                throw new InvalidOperationException("You can only set the SecurityService once per lifetime of this app.") ;
            _SecurityService = value ;
        }
    }
}

SecurityService может быть абстрактным сервисным фасадомe, чтобы его можно было расширить / заменить другой реализацией.

Есть ли лучший способ решить эту проблему?

ОБНОВЛЕНИЕ: добавление некоторого кода, чтобы показать, как я собираюсь это сделать:

Добавить открытое свойство в атрибуте, который возвращает имя разрешения:

public class ProtectedAttribute : ...
{
  private string _Permission ;
  public string Permission { get { return _Permission ; } /*...*/ }

  public ProtectedAttribute(string permission) { /*...*/ }
}

Установить фильтр авторизации и настроить зависимость через Ninject (при использовании Ninject):

using Ninject.Web.Mvc.FilterBindingSyntax;

public class MyModule : Ninject.Modules.NinjectModule
{
  public override void Load()
  {
    // mySecurityService instance below can have a singleton lifetime - perfect!
    this.BindFilter<MyAuthorizationFilter>(FilterScope.Action, 0)
        .WhenActionMethodHas<ProtectedAttribute>()
        .WithConstructorArgument("securityService", mySecurityService)
        .WithConstructorArgumentFromActionAttribute<ProtectedAttribute>("permission", p => p.PermissionName) ;
  }
}

Ооо, это ... прекрасно понюхать

Ответы [ 2 ]

7 голосов
/ 06 января 2012

В ASP.NET MVC 3 вы можете использовать инжектор конструктора с фильтрами действий благодаря новому IFilterProvider . Таким образом, вам больше не нужно украшать действия вашего контроллера фильтрами действий. Вы можете применять их благодаря этому интерфейсу и использованию атрибута маркера.

И если вы не хотите реализовывать его вручную, вы всегда можете использовать существующую структуру DI, такую ​​как Ninject, которая предоставляет свободный способ для определения зависимостей фильтра действий.

0 голосов
/ 06 января 2012

Мои приложения наследуются от базового класса Application, который предоставляет контейнер IOC.

public interface IInjectableApplication
    {
        IUnityContainer Container { get; }
    }

Тогда у меня есть базовый класс атрибутов, который знает об этом

public abstract IocAwareActionFilterAttribute : ActionFilterAttribute{
    protected T ResolveItem<T>(ResultExecutedContext context)
        {
            var app = context.HttpContext.ApplicationInstance as IInjectableApplication;
            if (app == null) { throw new NullReferenceException("Application is not IInjectable."); }

            T c = (T)app.Container.Resolve(typeof(T));

            if (c == null) { throw new NullReferenceException(string.Format("Could not find injected {0}.", typeof(T).FullName)); }
            return c;
        }

}

Хотя это не так, инъекция, поскольку атрибуты не создаются «нормально», это обеспечивает аналогичное поведение. Нет причин, по которым его нельзя адаптировать к другим МОК

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...