9/24/2008 12:14:00 PM

Вообще в Composite Web Application Block достаточно неплохо реализована авторизация (она базируется на Security-блоке из Enterprise Library). Но есть и недостаток в этой реализации: правила (rules) можно можно применять к ресурсам в рантайме, но декларировать (задавать) их можно только в конфигурационном файле.

Для тех, кто не сталкивался, объясню: правило (rule) - это некая именованая комбинация ролей и/или пользовательских имен. Например, можно создать правило, разрешающее управлять пользователями, которое будет определяться комбинацией "Роль Администратор или роль Менеджер или пользователь Вася Пупкин".
В конфигурации это задается так:

<rules>
    <add expression="R:Administrator OR R:Manager OR I:pupkin" name="AllowManageUsers"/>
</rules>

А используется это потом вот так, в конфигурационном файле:

<authorization>
   <rule Url="~/BackEnd/ManageUsers.aspx" Rule="AllowManageUsers"/>
</authorization>

Либо вот так, в рантайме:

authorizationRuleService.RegisterAuthorizationRule(url, ruleName);

Понятное дело, что сказав "а", хотелось бы и "б" сказать, то есть, иметь возможность "на лету" определять правила, а не только пользоваться уже имеющимися. Например, это было бы удобно делать при инициализации модуля (в наследнике ModuleInitializer), чтобы каждый модуль имел возможность задать свои собственные правила и применить их к своим ресурсам. Однако, как я уже и сказал, "out of box" такой возможности нет.

Однако, благодаря "сервисной" структуре Composite Web и Enterprise Library, ее можно добавить. Для этого нужно расширить поведение неустраивающих нас сервисов.

В нашем случае это AuthorizationRuleService из Composite Web, который отвечает за авторизацию, но не дает возможности зарегистрировать новое правило, и AuthorizationRuleProvider, который, собственно, и выполняет всю работу и который "заполняется" правилами из конфигурационного файла.

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

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

Для регистрации провайдера просто меняем тип в конфигурационном файле:

<securityConfiguration defaultAuthorizationInstance="RuleProvider" defaultSecurityCacheInstance="">
    <authorizationProviders>
        <add type="WebClientApplication.Shell.Providers.ShellAuthorizationRuleProvider, Shell" name="RuleProvider">
            <rules>
                <add expression="R:Administrator OR R:Manager OR I:pupkin" name="AllowManageUsers"/>
            </rules>
        </add>
    </authorizationProviders>
</securityConfiguration>

В случае сервиса идем в ModuleInitializer "главного" модуля (Shell), там есть функция AddGlobalServices, в которой регистрируется сервис IAuthorizationService. Выкидываем эту строчку, а вместо нее пишем:

IAuthorizationService newAuthService
    = globalServices.AddNew<ShellAuthorizationRuleService, IAuthorizationService>();

IRuleRegisterService ruleRegistrator = newAuthService as IRuleRegisterService;
if (ruleRegistrator != null) globalServices.Add<IRuleRegisterService>(ruleRegistrator);

Таким образом мы регистрируем один класс в виде сразу двух сервисов: IAuthorizationService (так, что подмены никто не заметит) и IRuleRegisterService, который позволит нам регистрировать правила "на лету".

Ну а пользоваться всем этим хозяйством - задача уже совсем рутинная:

 

public override void Load(CompositionContainer container)
{
    base.Load(container);

    AddGlobalServices(container.Parent.Services);
    AddModuleServices(container.Services);
    RegisterSiteMapInformation(container.Services.Get<ISiteMapBuilderService>(true));

    IRuleRegisterService ruleRegistrator = container.Services.Get<IRuleRegisterService>();
    if (ruleRegistrator != null) RegisterRules(ruleRegistrator);

    IAuthorizationRulesService rulesService = container.Services.Get<IAuthorizationRulesService>();
    if (rulesService != null) ApplyRules(rulesService);
}

private void ApplyRules(IAuthorizationRulesService rulesService)
{
    rulesService.RegisterAuthorizationRule("~/AdminPage.aspx", "OnlyForAdministrators");
}

private void RegisterRules(IRuleRegisterService ruleRegistrator)
{
    AuthorizationRule commonRule = new AuthorizationRule("OnlyForAdministrators", "R:Administrator");
    ruleRegistrator.RegisterRule("OnlyForAdministrators", commonRule);
}

Выделено то, что добавилось к уже имеющемуся коду инициализатора модуля. Там регистрируются правила (в моем случае всего одно: OnlyForAdministrators), а потом правила регистрируются для авторизации доступа к ресурсам.

WCABAuthorization.zip (4.09 kb)

Comments are closed

Powered by BlogEngine.NET 2.5.0.6

About the author

Alexey Raga Alexey Raga
.NET software developer.

E-mail me Send mail

Twitter


Recent posts

Archive

Disclaimer

The opinions expressed herein are my own personal opinions and do not represent my employer's view in anyway.

© Copyright 2012

Sign in