Авторизация на основе политик в ASP.NET Core

В процессе авторизации на основе ролей и авторизации на основе утверждений используются требования, обработчик требований и предварительно настроенная политика. Эти стандартные блоки поддерживают выражение оценки авторизации в коде. Результатом является расширенная, повторно используемая, тестируемая структура авторизации.

Политика авторизации включает одно или несколько требований. Он регистрируется как часть конфигурации службы авторизации в Startup.ConfigureServices методе:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });
}

В предыдущем примере создается политика "AtLeast21". Он имеет одно требование — с минимальным возрастом, которое предоставляется в качестве параметра для требования.

IAuthorizationService

Основная служба, которая определяет, успешно ли выполнена авторизация IAuthorizationService :

/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
    /// <summary>
    /// Checks if a user meets a specific set of requirements for the specified resource
    /// </summary>
    /// <param name="user">The user to evaluate the requirements against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="requirements">The requirements to evaluate.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// This value is <value>true</value> when the user fulfills the policy; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check 
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, 
                                     IEnumerable<IAuthorizationRequirement> requirements);

    /// <summary>
    /// Checks if a user meets a specific authorization policy
    /// </summary>
    /// <param name="user">The user to check the policy against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="policyName">The name of the policy to check against a specific 
    /// context.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// Returns a flag indicating whether the user, and optional resource has fulfilled 
    /// the policy.    
    /// <value>true</value> when the policy has been fulfilled; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(
                                ClaimsPrincipal user, object resource, string policyName);
}

В приведенном выше коде показаны два метода IAuthorizationService.

IAuthorizationRequirement — это служба маркеров без методов, а также механизм отслеживания успешности авторизации.

Каждый из них IAuthorizationHandler отвечает за проверку соблюдения требований:

/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
    /// <summary>
    /// Makes a decision if authorization is allowed.
    /// </summary>
    /// <param name="context">The authorization information.</param>
    Task HandleAsync(AuthorizationHandlerContext context);
}

Этот AuthorizationHandlerContext класс используется обработчиком для обозначения того, выполнены ли требования.

 context.Succeed(requirement)

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

public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, 
             object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
    // Create a tracking context from the authorization inputs.
    var authContext = _contextFactory.CreateContext(requirements, user, resource);

    // By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
    var handlers = await _handlers.GetHandlersAsync(authContext);

    // Invoke all handlers.
    foreach (var handler in handlers)
    {
        await handler.HandleAsync(authContext);
    }

    // Check the context, by default success is when all requirements have been met.
    return _evaluator.Evaluate(authContext);
}

В следующем коде показана типичная ConfigureServices :

public void ConfigureServices(IServiceCollection services)
{
    // Add all of your handlers to DI.
    services.AddSingleton<IAuthorizationHandler, MyHandler1>();
    // MyHandler2, ...

    services.AddSingleton<IAuthorizationHandler, MyHandlerN>();

    // Configure your policies
    services.AddAuthorization(options =>
          options.AddPolicy("Something",
          policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));


    services.AddControllersWithViews();
    services.AddRazorPages();
}

Используйте IAuthorizationService или [Authorize(Policy = "Something")] для авторизации.

Применение политик к контроллерам MVC

Если вы используете Razor страницы, см. раздел применение политик к Razor страницам этого документа.

Политики применяются к контроллерам с помощью [Authorize] атрибута с именем политики. Пример:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseController : Controller
{
    public IActionResult Index() => View();
}

Применение политик к Razor страницам

Политики применяются к Razor страницам с помощью [Authorize] атрибута с именем политики. Пример:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseModel : PageModel
{
}

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

Политики можно применять к Razor страницам с помощью соглашения об авторизации.

Требования

Требование авторизации — это коллекция параметров данных, которую политика может использовать для вычисления текущего субъекта-пользователя. В нашей политике "AtLeast21" требование — это единственный параметр — с минимальным возрастом. Требование реализует иаусоризатионрекуиремент, который является пустым интерфейсом маркера. Требование к параметризованному минимальному возрасту можно реализовать следующим образом:

using Microsoft.AspNetCore.Authorization;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

Если политика авторизации содержит несколько требований к авторизации, все требования должны пройти, чтобы оценка политики прошла успешно. Иными словами, несколько требований к авторизации, добавленных в одну политику авторизации, обрабатываются и на основе.

Примечание

Требование не обязательно должно иметь данные или свойства.

Обработчики авторизации

Обработчик авторизации отвечает за оценку свойств требования. Обработчик авторизации оценивает требования к предоставленному аусоризатионхандлерконтекст , чтобы определить, разрешен ли доступ.

Требование может иметь несколько обработчиков. Обработчик может наследовать AuthorizationHandler <TRequirement> , где TRequirement является требованием для обработки. Кроме того, обработчик может реализовать иаусоризатионхандлер для обработки более чем одного типа требований.

Использование обработчика для одного требования

Ниже приведен пример связи «один к одному», в которой обработчик минимального возраста использует одно требование:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                        c.Issuer == "http://contoso.com"))
        {
            //TODO: Use the following if targeting a version of
            //.NET Framework older than 4.6:
            //      return Task.FromResult(0);
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth && 
                                        c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Приведенный выше код определяет, имеет ли текущий участник пользователя дату утверждения о рождении, выданного известным и надежным издателем. Авторизация не может произойти, если утверждение отсутствует, в этом случае возвращается завершенная задача. При наличии утверждения возраст пользователя вычисляется. Если пользователь соответствует минимальному возрасту, заданному требованием, авторизация считается успешной. Когда авторизация прошла успешно, context.Succeed вызывается с удовлетворенным требованием в качестве единственного параметра.

Использование обработчика для нескольких требований

Ниже приведен пример связи «один ко многим», в которой обработчик разрешений может управлять тремя различными типами требований:

using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();

        foreach (var requirement in pendingRequirements)
        {
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource) ||
                    IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            else if (requirement is EditPermission ||
                     requirement is DeletePermission)
            {
                if (IsOwner(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }

    private bool IsOwner(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }

    private bool IsSponsor(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }
}

Предыдущий код проходит по пендингрекуирементс — свойству, содержащему требования, не помеченные как неудачные. Для ReadPermission получения требования пользователь должен быть владельцем или спонсором для доступа к запрошенному ресурсу. В случае EditPermission DeletePermission требования или ему необходимо быть владельцем для доступа к запрошенному ресурсу.

Регистрация обработчика

Обработчики регистрируются в коллекции служб во время настройки. Пример:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });

    services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}

Предыдущий код регистрируется MinimumAgeHandler как одноэлементный, вызывая services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); . Обработчики можно регистрировать с помощью любого встроенного времени существования службы.

Что должен возвращать обработчик?

Обратите внимание, что Handle метод в примере обработчика не возвращает значения. Как отображается состояние «успешно» или «сбой»?

  • Обработчик указывает на успешное выполнение путем вызова context.Succeed(IAuthorizationRequirement requirement) , передавая требование, которое прошло проверку.

  • Обработчику обычно не требуется обработка ошибок, так как другие обработчики для такого же требования могут быть выполнены удачно.

  • Чтобы гарантировать сбой, даже если другие обработчики требований выполняются успешно, вызовите context.Fail .

Если обработчик вызывает context.Succeed или context.Fail , все остальные обработчики все еще вызываются. Это позволяет создавать побочные эффекты, например ведение журнала, которое выполняется, даже если другой обработчик прошел проверку или не прошел требование. Если задано значение false , свойство инвокехандлерсафтерфаилуре сокращенно вызывает выполнение обработчиков при context.Fail вызове метода. InvokeHandlersAfterFailure по умолчанию имеет значение true , в этом случае вызываются все обработчики.

Примечание

Обработчики авторизации вызываются даже в случае сбоя проверки подлинности.

Зачем требуется несколько обработчиков для требования?

В случаях, когда требуется, чтобы оценка была в или на основе, реализуйте несколько обработчиков для одного требования. Например, в корпорации Майкрософт есть дверцы, которые открываются только с карточками ключей. Если вы оставите свой ключ дома, секретарь выводит временную наклейку и открывает дверцу. В этом сценарии у вас будет одно требование, буилдинжентри, но несколько обработчиков, каждый из которых проанализировать одно требование.

Буилдинжентрирекуиремент. CS

using Microsoft.AspNetCore.Authorization;

public class BuildingEntryRequirement : IAuthorizationRequirement
{
}

Баджеентрихандлер. CS

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "BadgeId" &&
                                       c.Issuer == "http://microsoftsecurity"))
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Темпораристиккерхандлер. CS

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, 
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" &&
                                       c.Issuer == "https://microsoftsecurity"))
        {
            // We'd also check the expiration date on the sticker.
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Убедитесь, что оба обработчика зарегистрированы. Если любой из обработчиков будет выполнен, когда политика оценивает BuildingEntryRequirement , то Вычисление политики будет завершено.

Использование Func для выполнения политики

Могут возникнуть ситуации, в которых выполнение политики может быть простым для выражения в коде. Func<AuthorizationHandlerContext, bool>При настройке политики с помощью RequireAssertion построителя политик можно указать.

Например, Предыдущая возможность BadgeEntryHandler может быть переписана следующим образом:

services.AddAuthorization(options =>
{
     options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "BadgeId" ||
                 c.Type == "TemporaryBadgeId") &&
                 c.Issuer == "https://microsoftsecurity")));
});

Доступ к контексту запроса MVC в обработчиках

HandleRequirementAsyncМетод, реализуемый в обработчике авторизации, имеет два параметра: и обрабатываемый объект AuthorizationHandlerContext TRequirement . Платформы, такие как MVC или SignalR , могут добавлять любой объект в свойство в Resource AuthorizationHandlerContext для передачи дополнительной информации.

При использовании маршрутизации конечных точек авторизация обычно обрабатывается по промежуточного слоя авторизации. В этом случае Resource свойство является экземпляром HttpContext . Контекст можно использовать для доступа к текущей конечной точке, которую можно использовать для проверки базового ресурса, с которым выполняется маршрутизация. Пример:

if (context.Resource is HttpContext httpContext)
{
    var endpoint = httpContext.GetEndpoint();
    var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
    ...
}

Если используется традиционная маршрутизация или когда Авторизация происходит как часть фильтра авторизации MVC, значение Resource является AuthorizationFilterContext экземпляром. Это свойство предоставляет доступ к HttpContext , RouteData и всем остальным, предоставляемым MVC и Razor страницами.

Использование Resource свойства зависит от платформы. Использование сведений в Resource свойстве ограничивает политики авторизации определенными платформами. Необходимо привести Resource свойство с помощью is ключевого слова, а затем подтвердить успешное приведение, чтобы гарантировать, что код не будет завершаться с ошибкой InvalidCastException при выполнении в других платформах:

// Requires the following import:
//     using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
    // Examine MVC-specific things like routing data.
}

Глобально требовать проверку подлинности всех пользователей

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

В процессе авторизации на основе ролей и авторизации на основе утверждений используются требования, обработчик требований и предварительно настроенная политика. Эти стандартные блоки поддерживают выражение оценки авторизации в коде. Результатом является расширенная, повторно используемая, тестируемая структура авторизации.

Политика авторизации включает одно или несколько требований. Он регистрируется как часть конфигурации службы авторизации в Startup.ConfigureServices методе:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });
}

В предыдущем примере создается политика "AtLeast21". Он имеет одно требование — с минимальным возрастом, которое предоставляется в качестве параметра для требования.

IAuthorizationService

Основная служба, которая определяет, успешно ли выполнена авторизация IAuthorizationService :

/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
    /// <summary>
    /// Checks if a user meets a specific set of requirements for the specified resource
    /// </summary>
    /// <param name="user">The user to evaluate the requirements against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="requirements">The requirements to evaluate.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// This value is <value>true</value> when the user fulfills the policy; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check 
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, 
                                     IEnumerable<IAuthorizationRequirement> requirements);

    /// <summary>
    /// Checks if a user meets a specific authorization policy
    /// </summary>
    /// <param name="user">The user to check the policy against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="policyName">The name of the policy to check against a specific 
    /// context.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// Returns a flag indicating whether the user, and optional resource has fulfilled 
    /// the policy.    
    /// <value>true</value> when the policy has been fulfilled; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(
                                ClaimsPrincipal user, object resource, string policyName);
}

В приведенном выше коде показаны два метода IAuthorizationService.

IAuthorizationRequirement — это служба маркеров без методов, а также механизм отслеживания успешности авторизации.

Каждый из них IAuthorizationHandler отвечает за проверку соблюдения требований:

/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
    /// <summary>
    /// Makes a decision if authorization is allowed.
    /// </summary>
    /// <param name="context">The authorization information.</param>
    Task HandleAsync(AuthorizationHandlerContext context);
}

Этот AuthorizationHandlerContext класс используется обработчиком для обозначения того, выполнены ли требования.

 context.Succeed(requirement)

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

public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, 
             object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
    // Create a tracking context from the authorization inputs.
    var authContext = _contextFactory.CreateContext(requirements, user, resource);

    // By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
    var handlers = await _handlers.GetHandlersAsync(authContext);

    // Invoke all handlers.
    foreach (var handler in handlers)
    {
        await handler.HandleAsync(authContext);
    }

    // Check the context, by default success is when all requirements have been met.
    return _evaluator.Evaluate(authContext);
}

В следующем коде показана типичная ConfigureServices :

public void ConfigureServices(IServiceCollection services)
{
    // Add all of your handlers to DI.
    services.AddSingleton<IAuthorizationHandler, MyHandler1>();
    // MyHandler2, ...

    services.AddSingleton<IAuthorizationHandler, MyHandlerN>();

    // Configure your policies
    services.AddAuthorization(options =>
          options.AddPolicy("Something",
          policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));


    services.AddControllersWithViews();
    services.AddRazorPages();
}

Используйте IAuthorizationService или [Authorize(Policy = "Something")] для авторизации.

Применение политик к контроллерам MVC

Если вы используете Razor страницы, см. раздел применение политик к Razor страницам этого документа.

Политики применяются к контроллерам с помощью [Authorize] атрибута с именем политики. Пример:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseController : Controller
{
    public IActionResult Index() => View();
}

Применение политик к Razor страницам

Политики применяются к Razor страницам с помощью [Authorize] атрибута с именем политики. Пример:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseModel : PageModel
{
}

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

Политики можно применять к Razor страницам с помощью соглашения об авторизации.

Требования

Требование авторизации — это коллекция параметров данных, которую политика может использовать для вычисления текущего субъекта-пользователя. В нашей политике "AtLeast21" требование — это единственный параметр — с минимальным возрастом. Требование реализует иаусоризатионрекуиремент, который является пустым интерфейсом маркера. Требование к параметризованному минимальному возрасту можно реализовать следующим образом:

using Microsoft.AspNetCore.Authorization;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

Если политика авторизации содержит несколько требований к авторизации, все требования должны пройти, чтобы оценка политики прошла успешно. Иными словами, несколько требований к авторизации, добавленных в одну политику авторизации, обрабатываются и на основе.

Примечание

Требование не обязательно должно иметь данные или свойства.

Обработчики авторизации

Обработчик авторизации отвечает за оценку свойств требования. Обработчик авторизации оценивает требования к предоставленному аусоризатионхандлерконтекст , чтобы определить, разрешен ли доступ.

Требование может иметь несколько обработчиков. Обработчик может наследовать AuthorizationHandler <TRequirement> , где TRequirement является требованием для обработки. Кроме того, обработчик может реализовать иаусоризатионхандлер для обработки более чем одного типа требований.

Использование обработчика для одного требования

Ниже приведен пример связи «один к одному», в которой обработчик минимального возраста использует одно требование:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                        c.Issuer == "http://contoso.com"))
        {
            //TODO: Use the following if targeting a version of
            //.NET Framework older than 4.6:
            //      return Task.FromResult(0);
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth && 
                                        c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Приведенный выше код определяет, имеет ли текущий участник пользователя дату утверждения о рождении, выданного известным и надежным издателем. Авторизация не может произойти, если утверждение отсутствует, в этом случае возвращается завершенная задача. При наличии утверждения возраст пользователя вычисляется. Если пользователь соответствует минимальному возрасту, заданному требованием, авторизация считается успешной. Когда авторизация прошла успешно, context.Succeed вызывается с удовлетворенным требованием в качестве единственного параметра.

Использование обработчика для нескольких требований

Ниже приведен пример связи «один ко многим», в которой обработчик разрешений может управлять тремя различными типами требований:

using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();

        foreach (var requirement in pendingRequirements)
        {
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource) ||
                    IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            else if (requirement is EditPermission ||
                     requirement is DeletePermission)
            {
                if (IsOwner(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }

    private bool IsOwner(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }

    private bool IsSponsor(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }
}

Предыдущий код проходит по пендингрекуирементс — свойству, содержащему требования, не помеченные как неудачные. Для ReadPermission получения требования пользователь должен быть владельцем или спонсором для доступа к запрошенному ресурсу. В случае EditPermission DeletePermission требования или ему необходимо быть владельцем для доступа к запрошенному ресурсу.

Регистрация обработчика

Обработчики регистрируются в коллекции служб во время настройки. Пример:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews();
    services.AddRazorPages();
    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });

    services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}

Предыдущий код регистрируется MinimumAgeHandler как одноэлементный, вызывая services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); . Обработчики можно регистрировать с помощью любого встроенного времени существования службы.

Что должен возвращать обработчик?

Обратите внимание, что Handle метод в примере обработчика не возвращает значения. Как отображается состояние «успешно» или «сбой»?

  • Обработчик указывает на успешное выполнение путем вызова context.Succeed(IAuthorizationRequirement requirement) , передавая требование, которое прошло проверку.

  • Обработчику обычно не требуется обработка ошибок, так как другие обработчики для такого же требования могут быть выполнены удачно.

  • Чтобы гарантировать сбой, даже если другие обработчики требований выполняются успешно, вызовите context.Fail .

Если обработчик вызывает context.Succeed или context.Fail , все остальные обработчики все еще вызываются. Это позволяет создавать побочные эффекты, например ведение журнала, которое выполняется, даже если другой обработчик прошел проверку или не прошел требование. Если задано значение false , свойство инвокехандлерсафтерфаилуре сокращенно вызывает выполнение обработчиков при context.Fail вызове метода. InvokeHandlersAfterFailure по умолчанию имеет значение true , в этом случае вызываются все обработчики.

Примечание

Обработчики авторизации вызываются даже в случае сбоя проверки подлинности.

Зачем требуется несколько обработчиков для требования?

В случаях, когда требуется, чтобы оценка была в или на основе, реализуйте несколько обработчиков для одного требования. Например, в корпорации Майкрософт есть дверцы, которые открываются только с карточками ключей. Если вы оставите свой ключ дома, секретарь выводит временную наклейку и открывает дверцу. В этом сценарии у вас будет одно требование, буилдинжентри, но несколько обработчиков, каждый из которых проанализировать одно требование.

Буилдинжентрирекуиремент. CS

using Microsoft.AspNetCore.Authorization;

public class BuildingEntryRequirement : IAuthorizationRequirement
{
}

Баджеентрихандлер. CS

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "BadgeId" &&
                                       c.Issuer == "http://microsoftsecurity"))
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Темпораристиккерхандлер. CS

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, 
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" &&
                                       c.Issuer == "https://microsoftsecurity"))
        {
            // We'd also check the expiration date on the sticker.
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Убедитесь, что оба обработчика зарегистрированы. Если любой из обработчиков будет выполнен, когда политика оценивает BuildingEntryRequirement , то Вычисление политики будет завершено.

Использование Func для выполнения политики

Могут возникнуть ситуации, в которых выполнение политики может быть простым для выражения в коде. Func<AuthorizationHandlerContext, bool>При настройке политики с помощью RequireAssertion построителя политик можно указать.

Например, Предыдущая возможность BadgeEntryHandler может быть переписана следующим образом:

services.AddAuthorization(options =>
{
     options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "BadgeId" ||
                 c.Type == "TemporaryBadgeId") &&
                 c.Issuer == "https://microsoftsecurity")));
});

Доступ к контексту запроса MVC в обработчиках

HandleRequirementAsyncМетод, реализуемый в обработчике авторизации, имеет два параметра: и обрабатываемый объект AuthorizationHandlerContext TRequirement . Платформы, такие как MVC или SignalR , могут добавлять любой объект в свойство в Resource AuthorizationHandlerContext для передачи дополнительной информации.

При использовании маршрутизации конечных точек авторизация обычно обрабатывается по промежуточного слоя авторизации. В этом случае Resource свойство является экземпляром Endpoint . Конечную точку можно использовать для проверки базового ресурса, с которым выполняется маршрутизация. Пример:

if (context.Resource is Endpoint endpoint)
{
   var actionDescriptor = endpoint.Metadata.GetMetadata<ControllerActionDescriptor>();
   ...
}

Конечная точка не предоставляет доступ к текущей HttpContext . При использовании маршрутизации конечных точек используйте IHttpContextAcessor для доступа HttpContext внутри обработчика авторизации. Дополнительные сведения см. в разделе Использование HttpContext из пользовательских компонентов.

Если используется традиционная маршрутизация или когда Авторизация происходит как часть фильтра авторизации MVC, значение Resource является AuthorizationFilterContext экземпляром. Это свойство предоставляет доступ к HttpContext , RouteData и всем остальным, предоставляемым MVC и Razor страницами.

Использование Resource свойства зависит от платформы. Использование сведений в Resource свойстве ограничивает политики авторизации определенными платформами. Необходимо привести Resource свойство с помощью is ключевого слова, а затем подтвердить успешное приведение, чтобы гарантировать, что код не будет завершаться с ошибкой InvalidCastException при выполнении в других платформах:

// Requires the following import:
//     using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
    // Examine MVC-specific things like routing data.
}

Глобально требовать проверку подлинности всех пользователей

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

В процессе авторизации на основе ролей и авторизации на основе утверждений используются требования, обработчик требований и предварительно настроенная политика. Эти стандартные блоки поддерживают выражение оценки авторизации в коде. Результатом является расширенная, повторно используемая, тестируемая структура авторизации.

Политика авторизации включает одно или несколько требований. Он регистрируется как часть конфигурации службы авторизации в Startup.ConfigureServices методе:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });
}

В предыдущем примере создается политика "AtLeast21". Он имеет одно требование — с минимальным возрастом, которое предоставляется в качестве параметра для требования.

IAuthorizationService

Основная служба, которая определяет, успешно ли выполнена авторизация IAuthorizationService :

/// <summary>
/// Checks policy based permissions for a user
/// </summary>
public interface IAuthorizationService
{
    /// <summary>
    /// Checks if a user meets a specific set of requirements for the specified resource
    /// </summary>
    /// <param name="user">The user to evaluate the requirements against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="requirements">The requirements to evaluate.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// This value is <value>true</value> when the user fulfills the policy; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check 
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, object resource, 
                                     IEnumerable<IAuthorizationRequirement> requirements);

    /// <summary>
    /// Checks if a user meets a specific authorization policy
    /// </summary>
    /// <param name="user">The user to check the policy against.</param>
    /// <param name="resource">
    /// An optional resource the policy should be checked with.
    /// If a resource is not required for policy evaluation you may pass null as the value
    /// </param>
    /// <param name="policyName">The name of the policy to check against a specific 
    /// context.</param>
    /// <returns>
    /// A flag indicating whether authorization has succeeded.
    /// Returns a flag indicating whether the user, and optional resource has fulfilled 
    /// the policy.    
    /// <value>true</value> when the policy has been fulfilled; 
    /// otherwise <value>false</value>.
    /// </returns>
    /// <remarks>
    /// Resource is an optional parameter and may be null. Please ensure that you check
    /// it is not null before acting upon it.
    /// </remarks>
    Task<AuthorizationResult> AuthorizeAsync(
                                ClaimsPrincipal user, object resource, string policyName);
}

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

В приведенном выше коде показаны два метода IAuthorizationService.

IAuthorizationRequirement — это служба маркеров без методов, а также механизм отслеживания успешности авторизации.

Каждый из них IAuthorizationHandler отвечает за проверку соблюдения требований:

/// <summary>
/// Classes implementing this interface are able to make a decision if authorization
/// is allowed.
/// </summary>
public interface IAuthorizationHandler
{
    /// <summary>
    /// Makes a decision if authorization is allowed.
    /// </summary>
    /// <param name="context">The authorization information.</param>
    Task HandleAsync(AuthorizationHandlerContext context);
}

Этот AuthorizationHandlerContext класс используется обработчиком для обозначения того, выполнены ли требования.

 context.Succeed(requirement)

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

public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user, 
             object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
    // Create a tracking context from the authorization inputs.
    var authContext = _contextFactory.CreateContext(requirements, user, resource);

    // By default this returns an IEnumerable<IAuthorizationHandlers> from DI.
    var handlers = await _handlers.GetHandlersAsync(authContext);

    // Invoke all handlers.
    foreach (var handler in handlers)
    {
        await handler.HandleAsync(authContext);
    }

    // Check the context, by default success is when all requirements have been met.
    return _evaluator.Evaluate(authContext);
}

В следующем коде показана типичная ConfigureServices :

public void ConfigureServices(IServiceCollection services)
{
    // Add all of your handlers to DI.
    services.AddSingleton<IAuthorizationHandler, MyHandler1>();
    // MyHandler2, ...

    services.AddSingleton<IAuthorizationHandler, MyHandlerN>();

    // Configure your policies
    services.AddAuthorization(options =>
          options.AddPolicy("Something",
          policy => policy.RequireClaim("Permission", "CanViewPage", "CanViewAnything")));


    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}

Используйте IAuthorizationService или [Authorize(Policy = "Something")] для авторизации.

Применение политик к контроллерам MVC

Если вы используете Razor страницы, см. раздел применение политик к Razor страницам этого документа.

Политики применяются к контроллерам с помощью [Authorize] атрибута с именем политики. Пример:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseController : Controller
{
    public IActionResult Index() => View();
}

Применение политик к Razor страницам

Политики применяются к Razor страницам с помощью [Authorize] атрибута с именем политики. Пример:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

[Authorize(Policy = "AtLeast21")]
public class AlcoholPurchaseModel : PageModel
{
}

Политики также можно применять к Razor страницам с помощью соглашения об авторизации.

Требования

Требование авторизации — это коллекция параметров данных, которую политика может использовать для вычисления текущего субъекта-пользователя. В нашей политике "AtLeast21" требование — это единственный параметр — с минимальным возрастом. Требование реализует иаусоризатионрекуиремент, который является пустым интерфейсом маркера. Требование к параметризованному минимальному возрасту можно реализовать следующим образом:

using Microsoft.AspNetCore.Authorization;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public int MinimumAge { get; }

    public MinimumAgeRequirement(int minimumAge)
    {
        MinimumAge = minimumAge;
    }
}

Если политика авторизации содержит несколько требований к авторизации, все требования должны пройти, чтобы оценка политики прошла успешно. Иными словами, несколько требований к авторизации, добавленных в одну политику авторизации, обрабатываются и на основе.

Примечание

Требование не обязательно должно иметь данные или свойства.

Обработчики авторизации

Обработчик авторизации отвечает за оценку свойств требования. Обработчик авторизации оценивает требования к предоставленному аусоризатионхандлерконтекст , чтобы определить, разрешен ли доступ.

Требование может иметь несколько обработчиков. Обработчик может наследовать AuthorizationHandler <TRequirement> , где TRequirement является требованием для обработки. Кроме того, обработчик может реализовать иаусоризатионхандлер для обработки более чем одного типа требований.

Использование обработчика для одного требования

Ниже приведен пример связи «один к одному», в которой обработчик минимального возраста использует одно требование:

using System;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   MinimumAgeRequirement requirement)
    {
        if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth &&
                                        c.Issuer == "http://contoso.com"))
        {
            //TODO: Use the following if targeting a version of
            //.NET Framework older than 4.6:
            //      return Task.FromResult(0);
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(
            context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth && 
                                        c.Issuer == "http://contoso.com").Value);

        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

        if (calculatedAge >= requirement.MinimumAge)
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Приведенный выше код определяет, имеет ли текущий участник пользователя дату утверждения о рождении, выданного известным и надежным издателем. Авторизация не может произойти, если утверждение отсутствует, в этом случае возвращается завершенная задача. При наличии утверждения возраст пользователя вычисляется. Если пользователь соответствует минимальному возрасту, заданному требованием, авторизация считается успешной. Когда авторизация прошла успешно, context.Succeed вызывается с удовлетворенным требованием в качестве единственного параметра.

Использование обработчика для нескольких требований

Ниже приведен пример связи «один ко многим», в которой обработчик разрешений может управлять тремя различными типами требований:

using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class PermissionHandler : IAuthorizationHandler
{
    public Task HandleAsync(AuthorizationHandlerContext context)
    {
        var pendingRequirements = context.PendingRequirements.ToList();

        foreach (var requirement in pendingRequirements)
        {
            if (requirement is ReadPermission)
            {
                if (IsOwner(context.User, context.Resource) ||
                    IsSponsor(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
            else if (requirement is EditPermission ||
                     requirement is DeletePermission)
            {
                if (IsOwner(context.User, context.Resource))
                {
                    context.Succeed(requirement);
                }
            }
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }

    private bool IsOwner(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }

    private bool IsSponsor(ClaimsPrincipal user, object resource)
    {
        // Code omitted for brevity

        return true;
    }
}

Предыдущий код проходит по пендингрекуирементс — свойству, содержащему требования, не помеченные как неудачные. Для ReadPermission получения требования пользователь должен быть владельцем или спонсором для доступа к запрошенному ресурсу. В случае EditPermission DeletePermission требования или ему необходимо быть владельцем для доступа к запрошенному ресурсу.

Регистрация обработчика

Обработчики регистрируются в коллекции служб во время настройки. Пример:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddAuthorization(options =>
    {
        options.AddPolicy("AtLeast21", policy =>
            policy.Requirements.Add(new MinimumAgeRequirement(21)));
    });

    services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
}

Предыдущий код регистрируется MinimumAgeHandler как одноэлементный, вызывая services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); . Обработчики можно регистрировать с помощью любого встроенного времени существования службы.

Что должен возвращать обработчик?

Обратите внимание, что Handle метод в примере обработчика не возвращает значения. Как отображается состояние «успешно» или «сбой»?

  • Обработчик указывает на успешное выполнение путем вызова context.Succeed(IAuthorizationRequirement requirement) , передавая требование, которое прошло проверку.

  • Обработчику обычно не требуется обработка ошибок, так как другие обработчики для такого же требования могут быть выполнены удачно.

  • Чтобы гарантировать сбой, даже если другие обработчики требований выполняются успешно, вызовите context.Fail .

Если обработчик вызывает context.Succeed или context.Fail , все остальные обработчики все еще вызываются. Это позволяет создавать побочные эффекты, например ведение журнала, которое выполняется, даже если другой обработчик прошел проверку или не прошел требование. Если задано значение false , свойство инвокехандлерсафтерфаилуре сокращенно вызывает выполнение обработчиков при context.Fail вызове метода. InvokeHandlersAfterFailure по умолчанию имеет значение true , в этом случае вызываются все обработчики.

Примечание

Обработчики авторизации вызываются даже в случае сбоя проверки подлинности.

Зачем требуется несколько обработчиков для требования?

В случаях, когда требуется, чтобы оценка была в или на основе, реализуйте несколько обработчиков для одного требования. Например, в корпорации Майкрософт есть дверцы, которые открываются только с карточками ключей. Если вы оставите свой ключ дома, секретарь выводит временную наклейку и открывает дверцу. В этом сценарии у вас будет одно требование, буилдинжентри, но несколько обработчиков, каждый из которых проанализировать одно требование.

Буилдинжентрирекуиремент. CS

using Microsoft.AspNetCore.Authorization;

public class BuildingEntryRequirement : IAuthorizationRequirement
{
}

Баджеентрихандлер. CS

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class BadgeEntryHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "BadgeId" &&
                                       c.Issuer == "http://microsoftsecurity"))
        {
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Темпораристиккерхандлер. CS

using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using PoliciesAuthApp1.Services.Requirements;

public class TemporaryStickerHandler : AuthorizationHandler<BuildingEntryRequirement>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, 
                                                   BuildingEntryRequirement requirement)
    {
        if (context.User.HasClaim(c => c.Type == "TemporaryBadgeId" &&
                                       c.Issuer == "https://microsoftsecurity"))
        {
            // We'd also check the expiration date on the sticker.
            context.Succeed(requirement);
        }

        //TODO: Use the following if targeting a version of
        //.NET Framework older than 4.6:
        //      return Task.FromResult(0);
        return Task.CompletedTask;
    }
}

Убедитесь, что оба обработчика зарегистрированы. Если любой из обработчиков будет выполнен, когда политика оценивает BuildingEntryRequirement , то Вычисление политики будет завершено.

Использование Func для выполнения политики

Могут возникнуть ситуации, в которых выполнение политики может быть простым для выражения в коде. Func<AuthorizationHandlerContext, bool>При настройке политики с помощью RequireAssertion построителя политик можно указать.

Например, Предыдущая возможность BadgeEntryHandler может быть переписана следующим образом:

services.AddAuthorization(options =>
{
     options.AddPolicy("BadgeEntry", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                (c.Type == "BadgeId" ||
                 c.Type == "TemporaryBadgeId") &&
                 c.Issuer == "https://microsoftsecurity")));
});

Доступ к контексту запроса MVC в обработчиках

HandleRequirementAsyncМетод, реализуемый в обработчике авторизации, имеет два параметра: и обрабатываемый объект AuthorizationHandlerContext TRequirement . Платформы, такие как MVC или SignalR , могут добавлять любой объект в свойство в Resource AuthorizationHandlerContext для передачи дополнительной информации.

Например, MVC передает экземпляр аусоризатионфилтерконтекст в Resource свойство. Это свойство предоставляет доступ к HttpContext , RouteData и всем остальным, предоставляемым MVC и Razor страницами.

Использование Resource свойства зависит от платформы. Использование сведений в Resource свойстве ограничивает политики авторизации определенными платформами. Необходимо привести Resource свойство с помощью is ключевого слова, а затем подтвердить успешное приведение, чтобы гарантировать, что код не будет завершаться с ошибкой InvalidCastException при выполнении в других платформах:

// Requires the following import:
//     using Microsoft.AspNetCore.Mvc.Filters;
if (context.Resource is AuthorizationFilterContext mvcContext)
{
    // Examine MVC-specific things like routing data.
}