Autorizace založená na zásadách v ASP.NET Core

Pod popisky se používá autorizace na základě role a autorizace na základě deklarací identity požadavek, obslužná rutina požadavku a předkonfigurovaná zásada. Tyto stavební bloky podporují výraz vyhodnocení autorizace v kódu. Výsledkem je bohatší, opakovaně použitelná a testovatelná struktura autorizace.

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

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á jediný požadavek – minimální věk, který je zadaný 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í toho, jestli je autorizace úspěšná.

Každý IAuthorizationHandler 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í, jestli byly splněny požadavky:

 context.Succeed(requirement)

Následující kód ukazuje zjednodušenou (anotovanou s komentáři) 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")));

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

Použití zásad na kontrolery MVC

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

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

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

Pokud se na úrovni kontroleru a akcí použije více zásad, musí se před udělením přístupu předat všechny zásady:

[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í atributu [Authorize] s názvem zásady. Pří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 Razor úrovni obslužné rutiny stránky, musí být použity na stránku.

Zásady lze také použít na Razor stránky pomocí autorizační konvence.

Použití zásad na koncové body

Použijte zásady na koncové body pomocí RequireAuthorization názvu zásady. Pří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žadavek jediným parametrem – minimální věk. Požadavek implementuje IAuthorizationRequirement, což je prázdné rozhraní značky. Parametrizovaný požadavek na minimální stáří lze 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í se všechny požadavky předat, aby vyhodnocení zásad proběhlo úspěšně. Jinými slovy, více požadavků na autorizaci přidaných do jedné zásady autorizace se zpracovává na základě operátoru 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 vyhodnocuje požadavky na zadané AuthorizationHandlerContext , aby určila, jestli je povolený přístup.

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 na zpracování. Případně může obslužná rutina implementovat IAuthorizationHandler přímo pro zpracování více typů požadavků.

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

Následující příklad ukazuje vztah 1:1, ve kterém obslužná rutina minimálního věku 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 narození, které vydal známý a důvěryhodný vystavitel. Autorizace nemůže nastat, když deklarace identity chybí, v takovém případě se vrátí dokončený úkol. Když je deklarace identity přítomen, vypočítá se věk uživatele. Pokud uživatel splňuje minimální věk definovaný požadavkem, považuje se autorizace za úspěšnou. Pokud je autorizace úspěšná, context.Succeed je vyvolána s splněným požadavkem jako jediným parametrem.

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

Následující příklad ukazuje relaci 1:N, ve které obslužná rutina oprávnění dokáže 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í PendingRequirements– vlastnost obsahující požadavky, které nejsou označené jako úspěšné. ReadPermission V případě požadavku musí být uživatel vlastníkem nebo sponzorem pro přístup k požadovanému prostředku. V případě EditPermission požadavku DeletePermission musí být vlastníkem pro přístup k požadovanému prostředku.

Registrace obslužné rutiny

Během konfigurace zaregistrujte obslužné rutiny v kolekci služeb. Příklad:

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

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

Je možné sbalit požadavek i obslužnou rutinu do jedné třídy, která implementuje obojí IAuthorizationRequirement i IAuthorizationHandler. Toto sdružování vytváří úzkou vazbu 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 zaregistrovat obslužnou rutinu v di kvůli integrované passThroughAuthorizationHandler, která umožňuje, aby se požadavky zpracovávaly samy.

Podívejte se na KontrolníRequirement třídy pro dobrý příklad, kde AssertionRequirement je požadavek i obslužná rutina v plně samostatné třídě.

Co má obslužná rutina vrátit?

Všimněte si, že Handle metoda v příkladuobslužné rutiny nevrací žádnou hodnotu. Jak je uveden stav úspěchu nebo selhání?

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

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

  • Chcete-li zaručit selhání, i když jiné obslužné rutiny požadavků jsou úspěšné, volání context.Fail.

Pokud obslužná rutina volá context.Succeed nebo context.Failse stále volají všechny ostatní obslužné rutiny. To umožňuje požadavkům vytvořit vedlejší účinky, jako je protokolování, které se provádí i v případě, že jiná obslužná rutina úspěšně ověřila nebo selhala požadavek. Při nastavení na vlastnost falseInvokeHandlersAfterFailure zkratuje provádění obslužných rutin při context.Fail volání. InvokeHandlersAfterFailure v takovém případě jsou volány všechny obslužné rutiny true.

Poznámka

Obslužné rutiny autorizace se volají i v případě, že ověřování selže. Obslužné rutiny se také můžou spouštět v libovolném pořadí, takže nezávisí na tom, že se volají v žádném konkrétním pořadí.

Proč bych pro požadavek chtěl více obslužných rutin?

V případech, kdy chcete vyhodnocení provést na základě nebo , implementujte pro jeden požadavek několik obslužných rutin. Microsoft má například dveře, které se otevírají jenom pomocí klávesových karet. Pokud necháte kartu s klíčem doma, recepční vytiskne dočasnou nálepku a otevře dveře za vás. 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 oba obslužné rutiny zaregistrované. Pokud je některý z obslužných rutin úspěšný při vyhodnocení BuildingEntryRequirementzásady , vyhodnocení zásady proběhne úspěšně.

Splnění zásad pomocí funcu

V situacích, kdy je plnění zásad jednoduché vyjádřit v kódu. Při konfiguraci zásad s RequireAssertion tvůrcem zásad je možné zadatFunc<AuthorizationHandlerContext, bool>.

Předchozí příklad je možné BadgeEntryHandler přepsat 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: metodu AuthorizationHandlerContext a zpracovávanou metodu TRequirement . Architektury, jako je MVC nebo SignalR jsou zdarma přidat jakýkoli objekt do Resource vlastnosti, AuthorizationHandlerContext aby bylo možno předat další informace.

Při použití směrování koncového bodu se autorizace obvykle zpracovává autorizačním middlewarem. V tomto případě Resource je vlastnost instancí HttpContext. Kontext se dá použít pro přístup k aktuálnímu koncovému bodu, který se dá použít k sondování základního prostředku, na který směrujete. Příklad:

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

U tradičního směrování nebo v případě, že se autorizace provádí jako součást autorizačního filtru MVC, je AuthorizationFilterContext hodnota Resource instance. Tato vlastnost poskytuje přístup k HttpContext, RouteDataa vše ostatní, co poskytuje MVC a Razor Pages.

Použití vlastnosti je specifické pro architekturu Resource . Použití informací ve Resource vlastnosti omezuje zásady autorizace na konkrétní architektury. Resource Přetypujte vlastnost pomocí klíčového is slova a ověřte, že přetypování proběhlo úspěšně, aby se zajistilo, že se váš kód při spuštění v jiných architekturách chybově nespustí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ů.

Autorizace pomocí ukázky externí služby

Ukázkový kód v AspNetCore.Docs.Samples ukazuje, jak implementovat další požadavky na autorizaci s externí autorizační službou. Ukázkový Contoso.API projekt je zabezpečený pomocí Azure AD. Další kontrola autorizace z Contoso.Security.API projektu vrátí datovou část popisující, jestli Contoso.API klientská aplikace může vyvolat GetWeather rozhraní API.

Konfigurace ukázky

  1. Vytvořte registraci aplikace v tenantovi Azure Active Directory (Azure AD):
  • Přiřaďte ji roli AppRole.
  • V části Oprávnění rozhraní API přidejte AppRole jako oprávnění a udělte Správa souhlas. Všimněte si, že v tomto nastavení představuje registrace aplikace rozhraní API i klienta, který rozhraní API vyvolává. Pokud chcete, můžete vytvořit dvě registrace aplikací. Pokud používáte toto nastavení, nezapomeňte oprávnění rozhraní API pouze provést, přidat AppRole jako krok oprávnění pouze pro klienta. K vygenerování tajného klíče klienta vyžaduje pouze registraci klientské aplikace.
  1. Contoso.API Nakonfigurujte projekt s následujícími nastaveními:
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<Tenant name from AAD properties>.onmicrosoft.com">,
    "TenantId": "<Tenant Id from AAD properties>",
    "ClientId": "<Client Id from App Registration representing the API>"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
  1. Nakonfigurujte Contoso.Security.API následující nastavení:
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "AllowedClients": [
    "<Use the appropriate Client Id representing the Client calling the API>"
  ]
}
  1. Naimportujte soubor ContosoAPI.postman_collection.json do nástroje Postman a nakonfigurujte prostředí následujícím kódem:

    • ClientId: ID klienta z registrace aplikace představujícího klienta, který volá rozhraní API.
    • clientSecret: Tajný klíč klienta z registrace aplikace představujícího klienta, který volá rozhraní API.
    • TenantId: ID tenanta z vlastností AAD
  2. Spusťte řešení a pomocí nástroje Postman vyvolejte rozhraní API. Do id klienta můžete přidat zarážky Contoso.Security.API.SecurityPolicyController a sledovat, že se předává ID klienta, které slouží k ověření, jestli je povoleno získat počasí.

Další prostředky

Pod popisky se používá autorizace na základě rolí a autorizace na základě deklarací identity požadavek, obslužná rutina požadavků a předem nakonfigurovaná zásada. Tyto stavební bloky podporují výraz vyhodnocení autorizace v kódu. Výsledkem je bohatší, opakovaně použitelná a 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á jediný požadavek – minimální věk, který je zadaný 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í toho, jestli je autorizace úspěšná.

Každý IAuthorizationHandler 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í, jestli byly splněny požadavky:

 context.Succeed(requirement)

Následující kód ukazuje zjednodušenou (anotovanou s komentáři) 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ý ConfigureServiceskód:

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

Použijte IAuthorizationService nebo [Authorize(Policy = "Something")] pro autorizaci.

Použití zásad na kontroler MVC

Pokud používáte Razor stránky, přečtěte si článek Použití zásad na Razor stránky v tomto dokumentu.

Zásady se použijí na kontrolery pomocí atributu [Authorize] s názvem zásady. Pří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í atributu [Authorize] s názvem zásady. Příklad:

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

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

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

Zásady se dají použít na Razor stránky pomocí autorizační konvence.

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žadavek jediným parametrem – minimální věk. Požadavek implementuje IAuthorizationRequirement, což je prázdné rozhraní značky. Parametrizovaný požadavek na minimální stáří lze 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í se všechny požadavky předat, aby vyhodnocení zásad proběhlo úspěšně. Jinými slovy, více požadavků na autorizaci přidaných do jedné zásady autorizace se zpracovává na základě operátoru 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 vyhodnocuje požadavky na zadané AuthorizationHandlerContext , aby určila, jestli je povolený přístup.

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 na zpracování. Případně může obslužná rutina implementovat IAuthorizationHandler zpracování více než jednoho typu požadavku.

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

Následující příklad ukazuje vztah 1:1, ve kterém obslužná rutina minimálního věku 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 narození, které vydal známý a důvěryhodný vystavitel. Autorizace nemůže nastat, když deklarace identity chybí, v takovém případě se vrátí dokončený úkol. Když je deklarace identity přítomen, vypočítá se věk uživatele. Pokud uživatel splňuje minimální věk definovaný požadavkem, považuje se autorizace za úspěšnou. Pokud je autorizace úspěšná, context.Succeed je vyvolána s splněným požadavkem jako jediným parametrem.

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

Následující příklad ukazuje relaci 1:N, ve které obslužná rutina oprávnění dokáže 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í PendingRequirements– vlastnost obsahující požadavky, které nejsou označené jako úspěšné. ReadPermission V případě požadavku musí být uživatel vlastníkem nebo sponzorem pro přístup k požadovanému prostředku. V případě EditPermission požadavku DeletePermission musí být uživatel vlastníkem pro přístup k požadovanému prostředku.

Registrace obslužné rutiny

Obslužné rutiny se během konfigurace registrují v kolekci služeb. Pří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í některé z předdefinovaných životností služeb.

Je možné sbalit požadavek i obslužnou rutinu do jedné třídy, která implementuje obojí IAuthorizationRequirement i IAuthorizationHandler. Toto sdružování vytváří úzkou vazbu 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 zaregistrovat obslužnou rutinu v di kvůli integrované passThroughAuthorizationHandler, která umožňuje, aby se požadavky zpracovávaly samy.

Podívejte se na KontrolníRequirement třídy pro dobrý příklad, kde AssertionRequirement je požadavek i obslužná rutina v plně samostatné třídě.

Co má obslužná rutina vrátit?

Všimněte si, že Handle metoda v příkladuobslužné rutiny nevrací žádnou hodnotu. Jak je uveden stav úspěchu nebo selhání?

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

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

  • Chcete-li zaručit selhání, i když jiné obslužné rutiny požadavků jsou úspěšné, volání context.Fail.

Pokud obslužná rutina volá context.Succeed nebo context.Failse stále volají všechny ostatní obslužné rutiny. To umožňuje požadavkům vytvořit vedlejší účinky, jako je protokolování, které se provádí i v případě, že jiná obslužná rutina úspěšně ověřila nebo selhala požadavek. Při nastavení na vlastnost falseInvokeHandlersAfterFailure zkratuje provádění obslužných rutin při context.Fail volání. InvokeHandlersAfterFailure v takovém případě jsou volány všechny obslužné rutiny true.

Poznámka

Obslužné rutiny autorizace se volají i v případě, že ověřování selže.

Proč bych pro požadavek chtěl více obslužných rutin?

V případech, kdy chcete vyhodnocení provést na základě nebo , implementujte pro jeden požadavek několik obslužných rutin. Microsoft má například dveře, které se otevírají jenom pomocí klávesových karet. Pokud necháte kartu s klíčem doma, recepční vytiskne dočasnou nálepku a otevře dveře za vás. 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 oba obslužné rutiny zaregistrované. Pokud je některý z obslužných rutin úspěšný při vyhodnocení BuildingEntryRequirementzásady , vyhodnocení zásady proběhne úspěšně.

Splnění zásad pomocí funcu

V situacích, kdy je plnění zásad jednoduché vyjádřit v kódu. Při konfiguraci zásad s tvůrcem RequireAssertion zásad je možné zadatFunc<AuthorizationHandlerContext, bool>.

Předchozí příklad je možné BadgeEntryHandler přepsat 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 AuthorizationHandlerContextTRequirement zpracováváte ji. Architektury, jako je MVC nebo SignalR jsou zdarma přidat jakýkoli objekt do Resource vlastnosti, AuthorizationHandlerContext aby bylo možno předat další informace.

Při použití směrování koncového bodu se autorizace obvykle zpracovává autorizačním middlewarem. V tomto případě Resource je vlastnost instancí HttpContext. Kontext se dá použít pro přístup k aktuálnímu koncovému bodu, který se dá použít k sondování základního prostředku, na který směrujete. Příklad:

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

U tradičního směrování nebo v případě, že se autorizace provádí jako součást autorizačního filtru MVC, je AuthorizationFilterContext hodnota Resource instance. Tato vlastnost poskytuje přístup k HttpContext, RouteDataa vše ostatní, co poskytuje MVC a Razor Pages.

Použití vlastnosti je specifické pro architekturu Resource . Použití informací ve Resource vlastnosti omezuje zásady autorizace na konkrétní architektury. Resource Přetypujte vlastnost pomocí klíčového is slova a ověřte, že přetypování proběhlo úspěšně, aby se zajistilo, že se váš kód při spuštění v jiných architekturách chybově nespustí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ů.