Вообще в 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)