Autorizace na základě zásad v ASP.NET Core

Uvnitř autorizace na základě rolí a autorizace na základě deklarací identity používají požadavek, obslužnou rutinu požadavku a předkonfigurované zásady. Tyto stavební bloky podporují výraz vyhodnocení autorizace v kódu. Výsledkem je bohatší, znovu použitelný, testovatelná struktura autorizace.

Zásady autorizace se skládají z jednoho nebo více požadavků. Zaregistrujte ho jako součást konfigurace autorizační služby v souboru Program.cs aplikace:

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

V předchozím příkladu se vytvoří zásada "AtLeast21". Má jeden požadavek — minimálního věku, který se dodá jako parametr požadavku.

IAuthorizationService

Primární služba, která určuje, jestli je autorizace úspěšná, je 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);
}

Předchozí kód zvýrazní dvě metody IAuthorizationService.

IAuthorizationRequirement je služba značek bez metod a mechanismus pro sledování, jestli je autorizace úspěšná.

Každá IAuthorizationHandler z nich zodpovídá za kontrolu splnění požadavků:

/// <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);
}

Třída AuthorizationHandlerContext je to, co obslužná rutina používá k označení, zda byly splněny požadavky:

 context.Succeed(requirement)

Následující kód ukazuje zjednodušenou (a anotovanou) výchozí implementaci autorizační služby:

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);
}

Následující kód ukazuje typickou konfiguraci autorizační služby:

// Add all of your handlers to DI.
builder.Services.AddSingleton<IAuthorizationHandler, MyHandler1>();
// MyHandler2, ...

builder.Services.AddSingleton<IAuthorizationHandler, MyHandlerN>();

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

K IAuthorizationService [Authorize(Policy = "Something")] autorizaci použijte , RequireAuthorization("Something") nebo .

Použití zásad na kontrolery MVC

Informace o Razor aplikacích, které používají stránky, najdete v části Použití zásad Razor na stránky.

Použijte zásady na kontrolery pomocí [Authorize] atributu s názvem zásady. Například:

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

namespace AuthorizationPoliciesSample.Controllers;

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

Použití zásad na Razor stránky

Použijte zásady na Razor stránky pomocí [Authorize] atributu s názvem zásady. Například:

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

namespace AuthorizationPoliciesSample.Pages;

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

Zásady nelze použít na úrovni obslužné Razor rutiny stránky, musí být použity na stránce.

Zásady je také možné použít na Razor stránky pomocí konvence autorizace.

Použití zásad pro koncové body

Použijte zásady pro koncové body s RequireAuthorization použitím názvu zásady. Například:

app.MapGet("/helloworld", () => "Hello World!")
    .RequireAuthorization("AtLeast21");

Požadavky

Požadavek na autorizaci je kolekce parametrů dat, které může zásada použít k vyhodnocení aktuálního objektu zabezpečení uživatele. V našich zásadách "AtLeast21" je požadavkem jeden parametr — minimální stáří. Požadavek implementuje IAuthorizationRequirement, což je prázdné rozhraní značky. Parametrizovaný požadavek na minimální stáří je možné implementovat takto:

using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Requirements;

public class MinimumAgeRequirement : IAuthorizationRequirement
{
    public MinimumAgeRequirement(int minimumAge) =>
        MinimumAge = minimumAge;

    public int MinimumAge { get; }
}

Pokud zásady autorizace obsahují více požadavků na autorizaci, musí být splněny všechny požadavky, aby vyhodnocení zásady bylo úspěšné. Jinými slovy, s více požadavky na autorizaci přidaných do jedné zásady autorizace se zachází na základě AND.

Poznámka

Požadavek nemusí mít data ani vlastnosti.

Obslužné rutiny autorizace

Obslužná rutina autorizace zodpovídá za vyhodnocení vlastností požadavku. Obslužná rutina autorizace vyhodnotí požadavky na poskytnutou hodnotu AuthorizationHandlerContext a určí, jestli je přístup povolený.

Požadavek může mít více obslužných rutin. Obslužná rutina může dědit AuthorizationHandler <TRequirement> , kde TRequirement je požadavek, který se má zpracovat. Alternativně může obslužná rutina implementovat IAuthorizationHandler přímo pro zpracování více než jednoho typu požadavku.

Použití obslužné rutiny pro jeden požadavek

Následující příklad ukazuje relaci 1:1, ve které obslužná rutina minimálního stáří zpracovává jeden požadavek:

using System.Security.Claims;
using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Handlers;

public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, MinimumAgeRequirement requirement)
    {
        var dateOfBirthClaim = context.User.FindFirst(
            c => c.Type == ClaimTypes.DateOfBirth && c.Issuer == "http://contoso.com");

        if (dateOfBirthClaim is null)
        {
            return Task.CompletedTask;
        }

        var dateOfBirth = Convert.ToDateTime(dateOfBirthClaim.Value);
        int calculatedAge = DateTime.Today.Year - dateOfBirth.Year;
        if (dateOfBirth > DateTime.Today.AddYears(-calculatedAge))
        {
            calculatedAge--;
        }

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

        return Task.CompletedTask;
    }
}

Předchozí kód určuje, jestli má aktuální objekt zabezpečení uživatele datum deklarace identity, která byla vydána známým a důvěryhodným vystavitelem. Autorizace nemůže nastat, když chybí deklarace identity. V takovém případě se vrátí dokončená úloha. Když je deklarace identity k dispozici, vypočítá se věk uživatele. Pokud uživatel splňuje minimální věk definovaný požadavkem, autorizace se považuje za úspěšnou. Po úspěšné autorizaci context.Succeed se vyvolá s jediným parametrem splněného požadavku.

Použití obslužné rutiny pro více požadavků

Následující příklad ukazuje relaci 1:N, ve které může obslužná rutina oprávnění zpracovat tři různé typy požadavků:

using System.Security.Claims;
using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Handlers;

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);
                }
            }
        }

        return Task.CompletedTask;
    }

    private static bool IsOwner(ClaimsPrincipal user, object? resource)
    {
        // Code omitted for brevity
        return true;
    }

    private static bool IsSponsor(ClaimsPrincipal user, object? resource)
    {
        // Code omitted for brevity
        return true;
    }
}

Předchozí kód prochází vlastnost PendingRequirements — obsahující požadavky, které nejsou označené jako úspěšné. Pro přístup k požadovanému prostředku musí být uživatel vlastníkem nebo ReadPermission sponzorem. V případě požadavku nebo musí být vlastníkem pro přístup EditPermission DeletePermission k požadovanému prostředku.

Registrace obslužné rutiny

Zaregistrujte obslužné rutiny v kolekci služeb během konfigurace. Například:

builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();

Předchozí kód se zaregistruje MinimumAgeHandler jako singleton. Obslužné rutiny je možné zaregistrovat pomocí kterékoli z předdefinovaných životností služby.

Požadavek i obslužnou rutinu je možné sesbalit do jedné třídy implementující jak IAuthorizationRequirement , tak IAuthorizationHandler i . Tím se vytvoří těsná párování mezi obslužnou rutinou a požadavkem a doporučuje se pouze pro jednoduché požadavky a obslužné rutiny. Vytvoření třídy, která implementuje obě rozhraní, eliminuje potřebu registrovat obslužnou rutinu v induicializačním objektu kvůli integrovanému objektu PassThroughAuthorizationHandler, který umožňuje požadavky na vlastní zpracování.

Dobrý příklad najdete ve třídě AssertionRequirement, kde je požadavek i obslužná rutina v plně AssertionRequirement samostatné třídě.

Co by měla obslužná rutina vrátit?

Všimněte Handle si, že metoda v příkladu obslužné rutiny nevrátí žádnou hodnotu. Jak je uveden stav úspěchů nebo neúspěchů?

  • Obslužná rutina označuje úspěch voláním context.Succeed(IAuthorizationRequirement requirement) metody , předáním požadavku, který byl úspěšně ověřen.

  • Obslužná rutina obecně nemusí zpracovávat selhání, protože jiné obslužné rutiny pro stejný požadavek mohou být úspěšné.

  • Pokud chcete zaručit selhání i v případě, že jiné obslužné rutiny požadavků prospějí, zavolejte context.Fail .

Pokud obslužná context.Succeed rutina volá nebo , jsou volány všechny ostatní context.Fail obslužné rutiny. To umožňuje, aby požadavky vedlo k vedlejším účinkům, jako je protokolování, ke kterému dochází i v případě, že požadavek úspěšně ověřila nebo selhala jiná obslužná rutina. Když je nastavená na , vlastnost false InvokeHandlersAfterFailure při volání zkrátí context.Fail provádění obslužných rutin. InvokeHandlersAfterFailure výchozí hodnota je true , v takovém případě jsou volány všechny obslužné rutiny.

Poznámka

Obslužné rutiny autorizace jsou volány i v případě, že ověření selže.

Proč je pro požadavek potřeba více obslužných rutin?

V případech, kdy chcete, aby vyhodnocení bylo na bázi OR, implementujte více obslužných rutin pro jeden požadavek. Microsoft má například dveře, které se otevřou jenom na kartách s klíči. Pokud si klíčovou kartu necháte doma, vytiskne si pohotový štítek a otevře pro vás dveře. V tomto scénáři byste měli jeden požadavek BuildingEntry, ale několik obslužných rutin, z nichž každá zkoumá jeden požadavek.

BuildingEntryRequirement.cs

using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Requirements;

public class BuildingEntryRequirement : IAuthorizationRequirement { }

BadgeEntryHandler.cs

using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Handlers;

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

        return Task.CompletedTask;
    }
}

TemporaryStickerHandler.cs

using AuthorizationPoliciesSample.Policies.Requirements;
using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Handlers;

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"))
        {
            // Code to check expiration date omitted for brevity.
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

Ujistěte se, že jsou zaregistrované obě obslužné rutiny. Pokud je některý z obslužných rutin úspěšný, když zásady vyhodnotí BuildingEntryRequirement , vyhodnocení zásady bude úspěšné.

Použití funkce ke splnění zásad

Může se zobrazit situace, kdy se naplnění zásady jednoduše vyjádřuje v kódu. Při konfiguraci zásad s tvůrcem zásad je možné Func<AuthorizationHandlerContext, bool> RequireAssertion zadat .

Předchozí kód může BadgeEntryHandler být například přepsán následujícím způsobem:

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

Přístup k kontextu požadavku MVC v obslužných rutinách

Metoda HandleRequirementAsync má dva parametry: a zpracovává AuthorizationHandlerContext TRequirement se. Architektury, jako je MVC nebo , mohou volně přidávat libovolný objekt do vlastnosti v pro SignalR Resource předání dalších AuthorizationHandlerContext informací.

Při použití směrování koncových bodů autorizace obvykle zpracovává autorizační middleware. V tomto případě Resource je vlastnost instancí HttpContext . Kontext lze použít pro přístup k aktuálnímu koncovému bodu, který se může použít k otesování základního prostředku, do kterého směrovat. Například:

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

Při tradičním směrování nebo při autorizaci v rámci autorizačního filtru MVC je hodnota Resource AuthorizationFilterContext instance. Tato vlastnost poskytuje přístup k HttpContext , a všemu ostatnímu, co poskytuje MVC a RouteData Razor Pages.

Použití vlastnosti je Resource specifické pro konkrétní rozhraní. Použití informací ve vlastnosti Resource omezuje vaše zásady autorizace na konkrétní architektury. Měli byste přetypovat vlastnost pomocí klíčového slova a ověřit, že přetypování proběhlo úspěšně, aby se zajistilo, že váš kód při spuštění v jiných architekturách nehavaruje s 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.
}

Globální vyžadování ověření všech uživatelů

Informace o tom, jak globálně vyžadovat ověření všech uživatelů, najdete v tématu vyžadování ověřených uživatelů.

Uvnitř autorizace na základě rolí a autorizace na základě deklarací identity používají požadavek, obslužnou rutinu požadavku a předem nakonfigurované zásady. Tyto stavební bloky podporují výraz vyhodnocení autorizace v kódu. Výsledkem je bohatší, znovu použitelný, testovatelná struktura autorizace.

Zásady autorizace se skládají z jednoho nebo více požadavků. Je zaregistrovaný jako součást konfigurace autorizační služby v Startup.ConfigureServices metodě :

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

V předchozím příkladu se vytvoří zásada "AtLeast21". Má jeden požadavek — minimálního věku, který se dodá jako parametr požadavku.

IAuthorizationService

Primární služba, která určuje, jestli je autorizace úspěšná, je 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);
}

Předchozí kód zvýrazní dvě metody IAuthorizationService.

IAuthorizationRequirement je služba značek bez metod a mechanismus pro sledování, jestli je autorizace úspěšná.

Každá IAuthorizationHandler z nich zodpovídá za kontrolu splnění požadavků:

/// <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);
}

Třída AuthorizationHandlerContext je to, co obslužná rutina používá k označení, zda byly splněny požadavky:

 context.Succeed(requirement)

Následující kód ukazuje zjednodušenou (a anotovanou) výchozí implementaci autorizační služby:

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);
}

Následující kód ukazuje typický typ 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();
}

K IAuthorizationService [Authorize(Policy = "Something")] autorizaci použijte nebo .

Použití zásad na kontrolery MVC

Pokud používáte stránky, podívejte se na stránku v tomto dokumentu v části Použití Razor zásad na stránky. Razor

Zásady se použijí na kontrolery pomocí [Authorize] atributu s názvem zásady. Například:

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

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

Použití zásad na Razor stránky

Zásady se použijí na Razor stránky pomocí [Authorize] atributu s názvem zásady. Například:

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

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

Zásady nelze použít na úrovni obslužné Razor rutiny stránky, musí být použity na stránce.

Zásady je možné použít na Razor stránky pomocí konvence autorizace.

Požadavky

Požadavek na autorizaci je kolekce parametrů dat, které může zásada použít k vyhodnocení aktuálního objektu zabezpečení uživatele. V našich zásadách "AtLeast21" je požadavkem jeden parametr — minimální stáří. Požadavek implementuje IAuthorizationRequirement, což je prázdné rozhraní značky. Parametrizovaný požadavek na minimální stáří je možné implementovat takto:

using Microsoft.AspNetCore.Authorization;

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

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

Pokud zásady autorizace obsahují více požadavků na autorizaci, musí být splněny všechny požadavky, aby vyhodnocení zásady bylo úspěšné. Jinými slovy, s více požadavky na autorizaci přidaných do jedné zásady autorizace se zachází na základě AND.

Poznámka

Požadavek nemusí mít data ani vlastnosti.

Obslužné rutiny autorizace

Obslužná rutina autorizace zodpovídá za vyhodnocení vlastností požadavku. Obslužná rutina autorizace vyhodnotí požadavky na poskytnutou hodnotu AuthorizationHandlerContext a určí, jestli je přístup povolený.

Požadavek může mít více obslužných rutin. Obslužná rutina může dědit AuthorizationHandler <TRequirement> , kde TRequirement je požadavek, který se má zpracovat. Alternativně může obslužná rutina implementovat IAuthorizationHandler pro zpracování více než jednoho typu požadavku.

Použití obslužné rutiny pro jeden požadavek

Následuje příklad relace 1:1, ve které obslužná rutina minimálního stáří využívá jeden požadavek:

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;
    }
}

Předchozí kód určuje, jestli má aktuální objekt zabezpečení uživatele datum deklarace identity, která byla vydána známým a důvěryhodným vystavitelem. Autorizace nemůže nastat, když chybí deklarace identity. V takovém případě se vrátí dokončená úloha. Když je deklarace identity k dispozici, vypočítá se věk uživatele. Pokud uživatel splňuje minimální věk definovaný požadavkem, autorizace se považuje za úspěšnou. Po úspěšné autorizaci context.Succeed se vyvolá s jediným parametrem splněného požadavku.

Použití obslužné rutiny pro více požadavků

Následuje příklad relace 1:N, ve které může obslužná rutina oprávnění zpracovat tři různé typy požadavků:

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;
    }
}

Předchozí kód prochází vlastnost PendingRequirements — obsahující požadavky, které nejsou označené jako úspěšné. Pro přístup k požadovanému prostředku musí být uživatel vlastníkem nebo ReadPermission sponzorem. V případě požadavku nebo musí být vlastníkem pro přístup EditPermission DeletePermission k požadovanému prostředku.

Registrace obslužné rutiny

Obslužné rutiny jsou registrovány v kolekci služeb během konfigurace. Například:

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>();
}

Předchozí kód se zaregistruje MinimumAgeHandler jako singleton vyvoláním services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); . Obslužné rutiny je možné zaregistrovat pomocí kterékoli z předdefinovaných životností služby.

Požadavek i obslužnou rutinu je možné sesbalovat do jedné třídy implementující jak IAuthorizationRequirement , tak IAuthorizationHandler i . Tím se vytvoří těsná párování mezi obslužnou rutinou a požadavkem a doporučuje se pouze pro jednoduché požadavky a obslužné rutiny. Vytvoření třídy, která implementuje obě rozhraní, eliminuje potřebu registrovat obslužnou rutinu v inicializačním objektu kvůli integrovanému objektu PassThroughAuthorizationHandler, který umožňuje požadavky na vlastní zpracování.

Dobrý příklad najdete ve třídě AssertionRequirement, kde je požadavek i obslužná rutina v AssertionRequirement plně samostatné třídě.

Co by měla obslužná rutina vrátit?

Všimněte Handle si, že metoda v příkladu obslužné rutiny nevrátí žádnou hodnotu. Jak je uveden stav úspěchů nebo neúspěchů?

  • Obslužná rutina označuje úspěch voláním context.Succeed(IAuthorizationRequirement requirement) metody , předáním požadavku, který byl úspěšně ověřen.

  • Obslužná rutina nemusí zpracovávat selhání obecně, protože jiné obslužné rutiny pro stejný požadavek mohou být úspěšné.

  • Pokud chcete zaručit selhání i v případě, že jiné obslužné rutiny požadavků prospějí, zavolejte context.Fail .

Pokud obslužná context.Succeed rutina volá nebo , jsou volány všechny ostatní context.Fail obslužné rutiny. To umožňuje, aby požadavky vedlo k vedlejším účinkům, jako je například protokolování, ke kterému dochází i v případě, že požadavek na jinou obslužnou rutinu úspěšně ověřila nebo selhala. Když je nastavená na , vlastnost false InvokeHandlersAfterFailure při volání zkrátí context.Fail provádění obslužných rutin. InvokeHandlersAfterFailure výchozí hodnota je true , v takovém případě jsou volány všechny obslužné rutiny.

Poznámka

Obslužné rutiny autorizace jsou volány i v případě, že se ověření nezdaří.

Proč je pro požadavek potřeba více obslužných rutin?

V případech, kdy chcete, aby vyhodnocení bylo na bázi OR, implementujte více obslužných rutin pro jeden požadavek. Microsoft má například dveře, které se otevřou jenom na kartách klíčů. Pokud si klíčovou kartu necháte doma, vytiskne si pohotový štítek a otevře pro vás dveře. V tomto scénáři byste měli jeden požadavek BuildingEntry, ale několik obslužných rutin, z nichž každá zkoumá jeden požadavek.

BuildingEntryRequirement.cs

using Microsoft.AspNetCore.Authorization;

public class BuildingEntryRequirement : IAuthorizationRequirement
{
}

BadgeEntryHandler.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;
    }
}

TemporaryStickerHandler.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;
    }
}

Ujistěte se, že jsou zaregistrované obě obslužné rutiny. Pokud je některý z obslužných rutin úspěšný, když zásady vyhodnotí BuildingEntryRequirement , vyhodnocení zásady bude úspěšné.

Použití funkce ke splnění zásad

Může se zobrazit situace, kdy se naplnění zásady jednoduše vyjádřuje v kódu. Při konfiguraci zásad s tvůrcem zásad je Func<AuthorizationHandlerContext, bool> RequireAssertion možné zadat .

Předchozí kód by BadgeEntryHandler například mohl být přepsán následujícím způsobem:

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

Přístup k kontextu požadavku MVC v obslužných rutinách

Metoda, HandleRequirementAsync kterou implementujete v obslužné rutině autorizace, má dva parametry: a , AuthorizationHandlerContext které TRequirement pracujete. Architektury, jako je MVC nebo , mohou volně přidávat libovolný objekt do vlastnosti v pro SignalR Resource předání dalších AuthorizationHandlerContext informací.

Při použití směrování koncových bodů autorizace obvykle zpracovává autorizační middleware. V tomto případě Resource je vlastnost instancí HttpContext . Kontext lze použít pro přístup k aktuálnímu koncovému bodu, který se může použít k otesování základního prostředku, do kterého směrovat. Například:

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

Při tradičním směrování nebo při autorizaci v rámci autorizačního filtru MVC je hodnota Resource AuthorizationFilterContext instance. Tato vlastnost poskytuje přístup k , a všemu ostatnímu, co poskytuje MVC a HttpContext RouteData Razor Pages.

Použití vlastnosti je Resource specifické pro konkrétní rozhraní. Použití informací ve vlastnosti Resource omezuje vaše zásady autorizace na konkrétní architektury. Měli byste přetypovat vlastnost pomocí klíčového slova a ověřit, že přetypování proběhlo úspěšně, aby se zajistilo, že váš kód při spuštění v jiných architekturách nehavaruje s 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.
}

Globální vyžadování ověření všech uživatelů

Informace o tom, jak globálně vyžadovat ověření všech uživatelů, najdete v tématu vyžadování ověřených uživatelů.

Uvnitř autorizace na základě rolí a autorizace na základě deklarací identity používají požadavek, obslužnou rutinu požadavku a předem nakonfigurované zásady. Tyto stavební bloky podporují výraz vyhodnocení autorizace v kódu. Výsledkem je bohatší, znovu použitelný, testovatelná struktura autorizace.

Zásady autorizace se skládají z jednoho nebo více požadavků. Je zaregistrovaný jako součást konfigurace autorizační služby v Startup.ConfigureServices metodě :

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

V předchozím příkladu se vytvoří zásada "AtLeast21". Má jeden požadavek — minimálního věku, který se dodá jako parametr požadavku.

IAuthorizationService

Primární služba, která určuje, jestli je autorizace úspěšná, je 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);
}

Předchozí kód zvýrazní dvě metody IAuthorizationService.

IAuthorizationRequirement je služba značek bez metod a mechanismus pro sledování, jestli je autorizace úspěšná.

Každá IAuthorizationHandler z nich zodpovídá za kontrolu splnění požadavků:

/// <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);
}

Třída AuthorizationHandlerContext je to, co obslužná rutina používá k označení, zda byly splněny požadavky:

 context.Succeed(requirement)

Následující kód ukazuje zjednodušenou (a anotovanou) výchozí implementaci autorizační služby:

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);
}

Následující kód ukazuje typický typ 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();
}

K IAuthorizationService [Authorize(Policy = "Something")] autorizaci použijte nebo .

Použití zásad na kontrolery MVC

Pokud používáte stránky, podívejte se na stránku v tomto dokumentu v části Použití Razor zásad na stránky. Razor

Zásady se použijí na kontrolery pomocí [Authorize] atributu s názvem zásady. Například:

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

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

Použití zásad na Razor stránky

Zásady se použijí na Razor stránky pomocí [Authorize] atributu s názvem zásady. Například:

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

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

Zásady nelze použít na úrovni obslužné Razor rutiny stránky, musí být použity na stránce.

Zásady je možné použít na Razor stránky pomocí konvence autorizace.

Požadavky

Požadavek na autorizaci je kolekce parametrů dat, které může zásada použít k vyhodnocení aktuálního objektu zabezpečení uživatele. V našich zásadách "AtLeast21" je požadavkem jeden parametr — minimální stáří. Požadavek implementuje IAuthorizationRequirement, což je prázdné rozhraní značky. Parametrizovaný požadavek na minimální stáří je možné implementovat takto:

using Microsoft.AspNetCore.Authorization;

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

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

Pokud zásady autorizace obsahují více požadavků na autorizaci, musí být splněny všechny požadavky, aby vyhodnocení zásady bylo úspěšné. Jinými slovy, s více požadavky na autorizaci přidaných do jedné zásady autorizace se zachází na základě AND.

Poznámka

Požadavek nemusí mít data ani vlastnosti.

Obslužné rutiny autorizace

Obslužná rutina autorizace zodpovídá za vyhodnocení vlastností požadavku. Obslužná rutina autorizace vyhodnotí požadavky na poskytnutou hodnotu AuthorizationHandlerContext a určí, jestli je přístup povolený.

Požadavek může mít více obslužných rutin. Obslužná rutina může dědit AuthorizationHandler <TRequirement> , kde TRequirement je požadavek, který se má zpracovat. Alternativně může obslužná rutina implementovat IAuthorizationHandler pro zpracování více než jednoho typu požadavku.

Použití obslužné rutiny pro jeden požadavek

Následuje příklad relace 1:1, ve které obslužná rutina minimálního stáří využívá jeden požadavek:

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;
    }
}

Předchozí kód určuje, jestli má aktuální objekt zabezpečení uživatele datum deklarace identity, která byla vydána známým a důvěryhodným vystavitelem. Autorizace nemůže nastat, když chybí deklarace identity. V takovém případě se vrátí dokončená úloha. Když je deklarace identity k dispozici, vypočítá se věk uživatele. Pokud uživatel splňuje minimální věk definovaný požadavkem, autorizace se považuje za úspěšnou. Po úspěšné autorizaci context.Succeed se vyvolá s jediným parametrem splněného požadavku.

Použití obslužné rutiny pro více požadavků

Následuje příklad relace 1:N, ve které může obslužná rutina oprávnění zpracovat tři různé typy požadavků:

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;
    }
}

Předchozí kód prochází vlastnost PendingRequirements — obsahující požadavky, které nejsou označené jako úspěšné. Pro přístup k požadovanému prostředku musí být uživatel vlastníkem nebo ReadPermission sponzorem. V případě požadavku nebo musí být vlastníkem pro přístup EditPermission DeletePermission k požadovanému prostředku.

Registrace obslužné rutiny

Obslužné rutiny jsou registrovány v kolekci služeb během konfigurace. Například:

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>();
}

Předchozí kód se zaregistruje MinimumAgeHandler jako singleton vyvoláním services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); . Obslužné rutiny je možné zaregistrovat pomocí kterékoli z předdefinovaných životností služby.

Co by měla obslužná rutina vrátit?

Všimněte Handle si, že metoda v příkladu obslužné rutiny nevrátí žádnou hodnotu. Jak je uveden stav úspěchů nebo neúspěchů?

  • Obslužná rutina označuje úspěch voláním context.Succeed(IAuthorizationRequirement requirement) metody , předáním požadavku, který byl úspěšně ověřen.

  • Obslužná rutina obecně nemusí zpracovávat selhání, protože jiné obslužné rutiny pro stejný požadavek mohou být úspěšné.

  • Pokud chcete zaručit selhání i v případě, že jiné obslužné rutiny požadavků prospějí, zavolejte context.Fail .

Pokud obslužná context.Succeed rutina volá nebo , jsou volány všechny ostatní context.Fail obslužné rutiny. To umožňuje, aby požadavky vedlo k vedlejším účinkům, jako je protokolování, ke kterému dochází i v případě, že požadavek úspěšně ověřila nebo selhala jiná obslužná rutina. Když je nastavená na , vlastnost false InvokeHandlersAfterFailure při volání zkrátí context.Fail provádění obslužných rutin. InvokeHandlersAfterFailure výchozí hodnota je true , v takovém případě jsou volány všechny obslužné rutiny.

Poznámka

Obslužné rutiny autorizace jsou volány i v případě, že ověření selže.

Proč je pro požadavek potřeba více obslužných rutin?

V případech, kdy chcete, aby vyhodnocení bylo na bázi OR, implementujte více obslužných rutin pro jeden požadavek. Microsoft má například dveře, které se otevřou jenom na kartách klíčů. Pokud si klíčovou kartu necháte doma, vytiskne si pohotový štítek a otevře pro vás dveře. V tomto scénáři byste měli jeden požadavek, BuildingEntry, ale více obslužných rutin, z nichž každá zkoumá jeden požadavek.

BuildingEntryRequirement.cs

using Microsoft.AspNetCore.Authorization;

public class BuildingEntryRequirement : IAuthorizationRequirement
{
}

BadgeEntryHandler.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;
    }
}

TemporaryStickerHandler.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;
    }
}

Ujistěte se, že jsou zaregistrované obě obslužné rutiny. Pokud je některý z obslužných rutin úspěšný, když zásady vyhodnotí BuildingEntryRequirement , vyhodnocení zásady bude úspěšné.

Použití funkce ke splnění zásad

Může se zobrazit situace, kdy se naplnění zásady jednoduše vyjádřuje v kódu. Při konfiguraci zásad s tvůrcem zásad je možné Func<AuthorizationHandlerContext, bool> RequireAssertion zadat .

Předchozí kód může BadgeEntryHandler být například přepsán následujícím způsobem:

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

Přístup k kontextu požadavku MVC v obslužných rutinách

Metoda, HandleRequirementAsync kterou implementujete v obslužné rutině autorizace, má dva parametry: a , AuthorizationHandlerContext které TRequirement pracujete. Architektury, jako je MVC nebo , mohou volně přidávat libovolný objekt do vlastnosti v pro SignalR Resource předání dalších AuthorizationHandlerContext informací.

Při použití směrování koncových bodů autorizace obvykle zpracovává autorizační middleware. V tomto případě Resource je vlastnost instancí Endpoint . Koncový bod je možné použít k otesování základního prostředku, do kterého směrovat. Například:

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

Koncový bod neposkytuje přístup k aktuálnímu bodu HttpContext . Při použití směrování koncového bodu použijte IHttpContextAcessor pro přístup v rámci obslužné rutiny HttpContext autorizace. Další informace najdete v tématu Použití HttpContext z vlastních komponent.

Při tradičním směrování nebo při autorizaci v rámci autorizačního filtru MVC je hodnota Resource AuthorizationFilterContext instance. Tato vlastnost poskytuje přístup k , a všemu ostatnímu, co poskytuje MVC a HttpContext RouteData Razor Pages.

Použití vlastnosti je Resource specifické pro konkrétní rozhraní. Použití informací ve vlastnosti Resource omezuje vaše zásady autorizace na konkrétní architektury. Měli byste přetypovat vlastnost pomocí klíčového slova a ověřit, že přetypování proběhlo úspěšně, aby se zajistilo, že váš kód při spuštění v jiných architekturách nehavaruje s 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.
}

Globální vyžadování ověření všech uživatelů

Informace o tom, jak globálně vyžadovat ověření všech uživatelů, najdete v tématu vyžadování ověřených uživatelů.