Создание политики со специальным требованием и обработчиком¶
Рассмотрим реализацию политики "CanAccessLounge". (Составляющие политики - на рисунке)

Создание IAuthorizationRequirement для представления требования¶
В терминах ASP.NET Core требование — это любой класс, реализующий интерфейс IAuthorizationRequirement. Это интерфейс-маркер, не имеющий методов.
Обычно, требования — это просто POCO-классы. Например, AllowedInLoungeRequirement может вообще не иметь методов или свойств:
Часто к требованию добавляют одно или несколько свойств, чтобы сделать его более универсальным. Например:
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public MinimumAgeRequirement(int minimumAge)
{
MinimumAge = minimumAge;
}
public int MinimumAge { get; }
}
Обработчики могут использовать свойства требования, чтобы определить, выполнено ли оно.
Создание политики с несколькими требованиями¶
Теперь настроим политику с этими требованиями:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthorization(options =>
{
options.AddPolicy( //Добавим простую политику
"CanEnterSecurity",
policyBuilder => policyBuilder
.RequireClaim(Claims.BoardingPassNumber));
options.AddPolicy(
"CanAccessLounge",
policyBuilder => policyBuilder.AddRequirements(
new MinimumAgeRequirement(18),
new AllowedInLoungeRequirement()
));
});
...
}
Теперь, когда мы добавили политики, мы можем использовать их названия в атрибуте
[Authorize]:[Authorize("CanAccessLounge")]
public class AirportLoungeModel : PageModel
{
public void OnGet() { }
}
Создаём обрабочики, чтобы отвечать требованиям¶
Обработчики авторизации содержат логику того, как соответствовать конкретной реализации IAuthorizationRequirement. При выполнении обработчик может делать одно из трёх:
- отметить обработку требований как успешную;
- ничего не делать;
- явно не отвечать требованию.
Обработчики должны быть унаследованы от AuthorizaionHandler<T>, где T - тип требования, которое они обладают:
public class FrequentFlyerHandler : AuthorizationHandler<AllowedInLoungeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
AllowedInLoungeRequirement requirement)
{
if (context.User.HasClaim("FrequentFlyerClass", "Gold"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
В случае успеха обработчик помечает требование как успешно выполненное, вызывая метод
context.Succeed() и передавая требование в качестве аргумента. Если у пользователя нет утверждения — обработчик ничего не делает.Такое поведение необходимо для реализации OR логики при композиции обработчиков.
Вот еще один обработчик для
AllowedInLoungeRequirement:public class IsAirlineEmployeeHandler : AuthorizationHandler<AllowedInLoungeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
AllowedInLoungeRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "EmployeeNumber"))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Для
MinimumAgeRequirement напишем обработчик, проверяющий, сколько лет прошло с момента рождения пользователя, и соответствует ли он требованию:public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
var dateOfBirthStr = context.User.FindFirstValue(ClaimTypes.DateOfBirth);
if (dateOfBirthStr == null)
{
return Task.CompletedTask;
}
var dateOfBirth = Convert.ToDateTime(dateOfBirthStr);
var cutoff = dateOfBirth.AddYears(requirement.MinimumAge);
if (cutoff < DateTime.Today)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
Иногда возможны ситуации, в которых мы хотим гарантировать неудачу проверки всей политики, несмотря на то, насколько были успешны другие обработчики (и требования). Для этого нужно вызвать метод context.Fail(), чтобы явно сделать авторизацию неудачной:
public class BannedFromLoungeHandler : AuthorizationHandler<AllowedInLoungeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationContext context,
AllowedInLoungeRequirement requirement)
{
if (context.User.HasClaim(c => c.Type == "IsBanned"))
{
context.Fail();
}
return Task.CompletedTask;
}
}
Примечание
Система авторизации всегда будет выполнять все обработчики для требования, и все требования к политике, независимо, вызывает ли какой-либо обработчик метод Succeed() или Fail().
Теперь надо зарегистрировать наши обработчики в контейнере внедрения зависимостей:
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthorization(options =>
{
options.AddPolicy( //Добавим простую политику
"CanEnterSecurity",
policyBuilder => policyBuilder
.RequireClaim(Claims.BoardingPassNumber));
options.AddPolicy(
"CanAccessLounge",
policyBuilder => policyBuilder.AddRequirements(
new MinimumAgeRequirement(18),
new AllowedInLoungeRequirement()
));
});
services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
services.AddSingleton<IAuthorizationHandler, FrequentFlyerHandler>();
services.AddSingleton<IAuthorizationHandler, BannedFromLoungeHandler>();
services.AddSingleton<IAuthorizationHandler, IsAirlineEmployeeHandler>();
...
}
Дата создания : 8 октября 2022 г.