Richtlinienbasierte Autorisierung in ASP.NET Core

Unter den Deckeln verwenden rollenbasierte Autorisierung und anspruchsbasierte Autorisierung eine Anforderung, einen Anforderungshandler und eine vorkonfigurierte Richtlinie. Diese Bausteine unterstützen den Ausdruck von Autorisierungsbewertungen im Code. Das Ergebnis ist eine umfassendere, wiederverwendbare, testbare Autorisierungsstruktur.

Eine Autorisierungsrichtlinie besteht aus mindestens einer Anforderung. Registrieren Sie sie als Teil der Autorisierungsdienstkonfiguration in der Datei der App Program.cs :

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

Im vorherigen Beispiel wird eine "AtLeast21"-Richtlinie erstellt. Es verfügt über eine einzelne Anforderung – die eines Mindestalters, das als Parameter für die Anforderung bereitgestellt wird.

IAuthorizationService

Der primäre Dienst, der bestimmt, ob die Autorisierung erfolgreich ist 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);
}

Der vorherige Code hebt die beiden Methoden des IAuthorizationService hervor.

IAuthorizationRequirement ist ein Markerdienst ohne Methoden und der Mechanismus zum Nachverfolgen, ob die Autorisierung erfolgreich ist.

Jeder IAuthorizationHandler ist verantwortlich für die Überprüfung, ob Anforderungen erfüllt sind:

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

Die AuthorizationHandlerContext Klasse verwendet den Handler, um zu markieren, ob anforderungen erfüllt wurden:

 context.Succeed(requirement)

Der folgende Code zeigt die vereinfachte (und mit Kommentaren versehene) Standardimplementierung des Autorisierungsdiensts:

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

Der folgende Code zeigt eine typische Autorisierungsdienstkonfiguration:

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

Verwenden Sie , oder [Authorize(Policy = "Something")]verwenden Sie IAuthorizationServiceRequireAuthorization("Something") eine Autorisierung.

Anwenden von Richtlinien auf MVC-Controller

Informationen zu Apps, die Seiten verwenden, finden Sie im Abschnitt "Anwenden von Richtlinien für Razor Seiten".Razor

Anwenden von Richtlinien auf Controller mithilfe des Attributs mit dem [Authorize] Richtliniennamen. Beispiel:

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

namespace AuthorizationPoliciesSample.Controllers;

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

Anwenden von Richtlinien auf Razor Seiten

Anwenden von Richtlinien auf Razor Seiten mithilfe des Attributs mit dem [Authorize] Richtliniennamen. Beispiel:

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

namespace AuthorizationPoliciesSample.Pages;

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

Richtlinien können aufRazor Seitenhandlerebene nicht angewendet werden, sie müssen auf die Seite angewendet werden.

Richtlinien können auch auf Razor Seiten angewendet werden, indem sie eine Autorisierungskonvention verwenden.

Anwenden von Richtlinien auf Endpunkte

Anwenden von Richtlinien auf Endpunkte mithilfe RequireAuthorization des Richtliniennamens. Beispiel:

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

Requirements (Anforderungen)

Eine Autorisierungsanforderung ist eine Sammlung von Datenparametern, die eine Richtlinie verwenden kann, um den aktuellen Benutzerprinzipal zu bewerten. In unserer Richtlinie "AtLeast21" ist die Anforderung ein einzelner Parameter – das Mindestalter. Eine Anforderung implementiert , die eine leere Markierungsschnittstelle IAuthorizationRequirementist. Eine parameterisierte Mindestaltersanforderung könnte wie folgt implementiert werden:

using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Requirements;

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

    public int MinimumAge { get; }
}

Wenn eine Autorisierungsrichtlinie mehrere Autorisierungsanforderungen enthält, müssen alle Anforderungen übergeben werden, damit die Richtlinienbewertung erfolgreich verläuft. Mit anderen Worten, mehrere Autorisierungsanforderungen, die einer einzigen Autorisierungsrichtlinie hinzugefügt wurden, werden auf EINER UND-Basis behandelt.

Hinweis

Eine Anforderung muss nicht über Daten oder Eigenschaften verfügen.

Autorisierungshandler

Ein Autorisierungshandler ist für die Auswertung der Eigenschaften einer Anforderung verantwortlich. Der Autorisierungshandler bewertet die Anforderungen anhand eines bereitgestellten AuthorizationHandlerContext Typs, um zu ermitteln, ob der Zugriff zulässig ist.

Eine Anforderung kann mehrere Handler aufweisen. Ein Handler kann erben AuthorizationHandler<TRequirement>, wo TRequirement die Anforderung ist, behandelt zu werden. Alternativ kann ein Handler direkt implementieren IAuthorizationHandler , um mehrere Arten von Anforderungen zu behandeln.

Verwenden eines Handlers für eine Anforderung

Im folgenden Beispiel wird eine 1-1-Beziehung gezeigt, in der ein Mindestaltershandler eine einzelne Anforderung behandelt:

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

Der vorherige Code bestimmt, ob der aktuelle Benutzerprinzipal über ein Geburtsdatum verfügt, das von einem bekannten und vertrauenswürdigen Aussteller ausgestellt wurde. Die Autorisierung kann nicht auftreten, wenn der Anspruch fehlt, in diesem Fall wird eine abgeschlossene Aufgabe zurückgegeben. Wenn ein Anspruch vorhanden ist, wird das Alter des Benutzers berechnet. Wenn der Benutzer das mindestalter erfüllt, das durch die Anforderung definiert ist, wird die Autorisierung als erfolgreich betrachtet. Wenn die Autorisierung erfolgreich ist, context.Succeed wird mit der erfüllten Anforderung als einziger Parameter aufgerufen.

Verwenden eines Handlers für mehrere Anforderungen

Im folgenden Beispiel wird eine 1-zu-viele Beziehung gezeigt, in der ein Berechtigungshandler drei verschiedene Arten von Anforderungen behandeln kann:

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

Der vorherige Code durchläuft PendingRequirements– eine Eigenschaft, die Anforderungen enthält, die nicht als erfolgreich gekennzeichnet sind. Für eine ReadPermission Anforderung muss der Benutzer entweder Besitzer oder Sponsor sein, um auf die angeforderte Ressource zuzugreifen. Für eine EditPermission oder DeletePermission Anforderung muss es sich um einen Besitzer handelt, um auf die angeforderte Ressource zuzugreifen.

Registrierung des Handlers

Registrieren von Handlern in der Dienstsammlung während der Konfiguration. Beispiel:

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

Der vorherige Code registriert MinimumAgeHandler sich als Singleton. Handler können mithilfe einer der integrierten Dienstlebensdauern registriert werden.

Es ist möglich, sowohl eine Anforderung als auch einen Handler in eine einzelne Klasse zu bündeln, die sowohl IAuthorizationRequirementIAuthorizationHandlerimplementiert wird als auch . Diese Bündelung erstellt eine enge Kopplung zwischen dem Handler und der Anforderung und wird nur für einfache Anforderungen und Handler empfohlen. Das Erstellen einer Klasse, die beide Schnittstellen implementiert, entfernt die Notwendigkeit, den Handler in DI zu registrieren, da der integrierte PassThroughAuthorizationHandler die Anforderungen selbst behandeln kann.

Sehen Sie sich die AssertionRequirement-Klasse für ein gutes Beispiel an, in dem es AssertionRequirement sich sowohl um eine Anforderung als auch um den Handler in einer vollständig eigenständigen Klasse handelt.

Was sollte ein Handler zurückgeben?

Beachten Sie, dass die Handle Methode im Handlerbeispiel keinen Wert zurückgibt. Wie ist ein Status eines Erfolgs oder eines Fehlers angegeben?

  • Ein Handler gibt Erfolg an, indem er aufruft context.Succeed(IAuthorizationRequirement requirement), die Anforderung übergeben, die erfolgreich überprüft wurde.

  • Ein Handler muss im Allgemeinen keine Fehler behandeln, da andere Handler für dieselbe Anforderung möglicherweise erfolgreich sein können.

  • Um Fehler zu gewährleisten, auch wenn andere Anforderungshandler erfolgreich sind, rufen Sie auf context.Fail.

Wenn ein Handler aufruft oder context.Fail, werden alle anderen Handler weiterhin aufgerufencontext.Succeed. Dies ermöglicht es Anforderungen, Nebenwirkungen zu erzeugen, z. B. Protokollierung, die selbst dann erfolgen, wenn ein anderer Handler erfolgreich überprüft oder eine Anforderung fehlgeschlagen ist. Beim Festlegen auf false, wird die InvokeHandlersAfterFailure Ausführung von Handlern, wenn context.Fail die Eigenschaft aufgerufen wird, kurzschaltungen. InvokeHandlersAfterFailure standardmäßig auf true, in welchem Fall alle Handler aufgerufen werden.

Hinweis

Autorisierungshandler werden auch dann aufgerufen, wenn die Authentifizierung fehlschlägt.

Warum möchte ich mehrere Handler für eine Anforderung?

In Fällen, in denen die Auswertung auf einer OR-Basis erfolgen soll, implementieren Sie mehrere Handler für eine einzelne Anforderung. Microsoft verfügt z. B. über Türen, die nur mit Schlüsselkarten geöffnet werden. Wenn Sie Ihre Schlüsselkarte zu Hause verlassen, druckt der Rezeptionist einen temporären Aufkleber und öffnet die Tür für Sie. In diesem Szenario haben Sie eine einzelne Anforderung, BuildingEntry, aber mehrere Handler, die jeweils eine einzelne Anforderung untersuchen.

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

Stellen Sie sicher, dass beide Handler registriert sind. Wenn ein Handler erfolgreich ist, wenn eine Richtlinie die BuildingEntryRequirementBewertung auswertet, ist die Richtlinienbewertung erfolgreich.

Verwenden eines Func zum Erfüllen einer Richtlinie

Möglicherweise gibt es Situationen, in denen die Erfüllung einer Richtlinie einfach im Code ausgedrückt werden kann. Es ist möglich, eine Func<AuthorizationHandlerContext, bool> Richtlinie beim Konfigurieren einer Richtlinie mit dem RequireAssertion Richtlinien-Generator zu bereitstellen.

Beispielsweise könnte der vorherige BadgeEntryHandler Vorgang wie folgt neu geschrieben werden:

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

Zugriff auf MVC-Anforderungskontext in Handlern

Die HandleRequirementAsync Methode weist zwei Parameter auf: eine AuthorizationHandlerContext und die TRequirement behandelte Methode. Frameworks, z. B. MVC, SignalR können der Eigenschaft AuthorizationHandlerContext ein beliebiges Objekt Resource hinzufügen, um zusätzliche Informationen zu übergeben.

Bei Verwendung des Endpunktroutings wird die Autorisierung in der Regel von der Autorisierungs-Middleware behandelt. In diesem Fall ist die Resource Eigenschaft eine Instanz von HttpContext. Der Kontext kann verwendet werden, um auf den aktuellen Endpunkt zuzugreifen, der verwendet werden kann, um die zugrunde liegende Ressource zu untersuchen, an die Sie routingen. Beispiel:

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

Bei herkömmlichem Routing oder wenn die Autorisierung als Teil des Autorisierungsfilters von MVC erfolgt, ist der Wert Resource einer AuthorizationFilterContext Instanz. Diese Eigenschaft bietet Zugriff auf HttpContext, RouteDataund alles andere, das von MVC und Razor Pages bereitgestellt wird.

Die Verwendung der Resource Eigenschaft ist frameworkspezifisch. Die Verwendung von Informationen in der Resource Eigenschaft beschränkt Ihre Autorisierungsrichtlinien auf bestimmte Frameworks. Casten Sie die Eigenschaft mithilfe des is Schlüsselworts, und bestätigen Sie dann, dass das Resource Cast erfolgreich war, um sicherzustellen, dass Ihr Code nicht mit einem InvalidCastException Absturz abstürzt, wenn sie auf anderen Frameworks ausgeführt wird:

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

Global müssen alle Benutzer authentifiziert werden.

Informationen dazu, wie global die Authentifizierung aller Benutzer angefordert werden kann, finden Sie unter Authentifizierte Benutzer erforderlich.

Unter den Deckeln verwenden rollenbasierte Autorisierung und anspruchsbasierte Autorisierung eine Anforderung, einen Anforderungshandler und eine vorkonfigurierte Richtlinie. Diese Bausteine unterstützen den Ausdruck von Autorisierungsbewertungen im Code. Das Ergebnis ist eine umfassendere, wiederverwendbare, testbare Autorisierungsstruktur.

Eine Autorisierungsrichtlinie besteht aus mindestens einer Anforderung. Es wird als Teil der Autorisierungsdienstkonfiguration in der Startup.ConfigureServices Methode registriert:

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

Im vorherigen Beispiel wird eine "AtLeast21"-Richtlinie erstellt. Es verfügt über eine einzelne Anforderung – die eines Mindestalters, das als Parameter für die Anforderung bereitgestellt wird.

IAuthorizationService

Der primäre Dienst, der bestimmt, ob die Autorisierung erfolgreich ist 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);
}

Der vorherige Code hebt die beiden Methoden des IAuthorizationService hervor.

IAuthorizationRequirement ist ein Markerdienst ohne Methoden und der Mechanismus zum Nachverfolgen, ob die Autorisierung erfolgreich ist.

Jeder IAuthorizationHandler ist verantwortlich für die Überprüfung, ob Anforderungen erfüllt sind:

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

Die AuthorizationHandlerContext Klasse verwendet den Handler, um zu markieren, ob anforderungen erfüllt wurden:

 context.Succeed(requirement)

Der folgende Code zeigt die vereinfachte (und mit Kommentaren versehene) Standardimplementierung des Autorisierungsdiensts:

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

Der folgende Code zeigt ein typisches 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();
}

Verwenden IAuthorizationService oder [Authorize(Policy = "Something")] für die Autorisierung.

Anwenden von Richtlinien auf MVC-Controller

Wenn Sie Seiten verwenden, finden Sie unter "Anwenden von Richtlinien auf Razor Seiten" in diesem Dokument.Razor

Die Richtlinien werden auf den Controller angewendet, indem das [Authorize]-Attribut mit dem Richtliniennamen verwendet wird. Beispiel:

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

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

Anwenden von Richtlinien auf Razor Seiten

Richtlinien werden auf Razor Seiten angewendet, indem sie das Attribut mit dem [Authorize] Richtliniennamen verwenden. Beispiel:

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

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

Richtlinien können aufRazor Seitenhandlerebene nicht angewendet werden, sie müssen auf die Seite angewendet werden.

Richtlinien können mithilfe einer Autorisierungskonvention auf Razor Seiten angewendet werden.

Requirements (Anforderungen)

Eine Autorisierungsanforderung ist eine Sammlung von Datenparametern, die eine Richtlinie verwenden kann, um den aktuellen Benutzerprinzipal zu bewerten. In unserer Richtlinie "AtLeast21" ist die Anforderung ein einzelner Parameter – das Mindestalter. Eine Anforderung implementiert , die eine leere Markierungsschnittstelle IAuthorizationRequirementist. Eine parameterisierte Mindestaltersanforderung könnte wie folgt implementiert werden:

using Microsoft.AspNetCore.Authorization;

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

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

Wenn eine Autorisierungsrichtlinie mehrere Autorisierungsanforderungen enthält, müssen alle Anforderungen übergeben werden, damit die Richtlinienbewertung erfolgreich verläuft. Mit anderen Worten, mehrere Autorisierungsanforderungen, die einer einzigen Autorisierungsrichtlinie hinzugefügt wurden, werden auf EINER UND-Basis behandelt.

Hinweis

Eine Anforderung muss nicht über Daten oder Eigenschaften verfügen.

Autorisierungshandler

Ein Autorisierungshandler ist für die Auswertung der Eigenschaften einer Anforderung verantwortlich. Der Autorisierungshandler bewertet die Anforderungen anhand eines bereitgestellten AuthorizationHandlerContext Typs, um zu ermitteln, ob der Zugriff zulässig ist.

Eine Anforderung kann mehrere Handler aufweisen. Ein Handler kann erben AuthorizationHandler<TRequirement>, wo TRequirement die Anforderung ist, behandelt zu werden. Alternativ kann ein Handler mehr als einen Anforderungstyp implementieren IAuthorizationHandler .

Verwenden eines Handlers für eine Anforderung

Im folgenden Beispiel wird eine 1-1-Beziehung gezeigt, in der ein Mindestaltershandler eine einzelne Anforderung verwendet:

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

Der vorherige Code bestimmt, ob der aktuelle Benutzerprinzipal über ein Geburtsdatum verfügt, das von einem bekannten und vertrauenswürdigen Aussteller ausgestellt wurde. Die Autorisierung kann nicht auftreten, wenn der Anspruch fehlt, in diesem Fall wird eine abgeschlossene Aufgabe zurückgegeben. Wenn ein Anspruch vorhanden ist, wird das Alter des Benutzers berechnet. Wenn der Benutzer das mindestalter erfüllt, das durch die Anforderung definiert ist, wird die Autorisierung als erfolgreich betrachtet. Wenn die Autorisierung erfolgreich ist, context.Succeed wird mit der erfüllten Anforderung als einziger Parameter aufgerufen.

Verwenden eines Handlers für mehrere Anforderungen

Im folgenden Beispiel wird eine 1-zu-viele Beziehung gezeigt, in der ein Berechtigungshandler drei verschiedene Arten von Anforderungen behandeln kann:

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

Der vorherige Code durchläuft PendingRequirements– eine Eigenschaft, die Anforderungen enthält, die nicht als erfolgreich gekennzeichnet sind. Für eine ReadPermission Anforderung muss der Benutzer entweder Besitzer oder Sponsor sein, um auf die angeforderte Ressource zuzugreifen. Für eine EditPermission oder DeletePermission Anforderung muss der Benutzer ein Besitzer sein, um auf die angeforderte Ressource zuzugreifen.

Registrierung des Handlers

Handler werden während der Konfiguration in der Dienstsammlung registriert. Beispiel:

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

Der vorherige Code registriert MinimumAgeHandler sich als Singleton durch Aufrufen services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();. Handler können mithilfe einer der integrierten Dienstlebensdauern registriert werden.

Es ist möglich, sowohl einen Anforderungs- als auch einen Handler in einer einzelnen Klasse zu bündeln, die sowohl IAuthorizationRequirementIAuthorizationHandlerimplementiert wird als auch . Diese Bündelung erstellt eine enge Kopplung zwischen dem Handler und der Anforderung und wird nur für einfache Anforderungen und Handler empfohlen. Das Erstellen einer Klasse, die beide Schnittstellen implementiert, entfernt die Notwendigkeit, den Handler in DI zu registrieren, da der integrierte PassThroughAuthorizationHandler die Anforderungen selbst behandeln kann.

Sehen Sie sich die AssertionRequirement-Klasse für ein gutes Beispiel an, in dem es AssertionRequirement sich sowohl um eine Anforderung als auch um den Handler in einer vollständig eigenständigen Klasse handelt.

Was sollte ein Handler zurückgeben?

Beachten Sie, dass die Handle Methode im Handlerbeispiel keinen Wert zurückgibt. Wie ist ein Status eines Erfolgs oder eines Fehlers angegeben?

  • Ein Handler gibt Erfolg an, indem er aufruft context.Succeed(IAuthorizationRequirement requirement), die Anforderung übergeben, die erfolgreich überprüft wurde.

  • Ein Handler muss im Allgemeinen keine Fehler behandeln, da andere Handler für dieselbe Anforderung möglicherweise erfolgreich sein können.

  • Um Fehler zu gewährleisten, auch wenn andere Anforderungshandler erfolgreich sind, rufen Sie auf context.Fail.

Wenn ein Handler aufruft oder context.Fail, werden alle anderen Handler weiterhin aufgerufencontext.Succeed. Dies ermöglicht es Anforderungen, Nebenwirkungen zu erzeugen, z. B. Protokollierung, die selbst dann erfolgen, wenn ein anderer Handler erfolgreich überprüft oder eine Anforderung fehlgeschlagen ist. Beim Festlegen auf false, wird die InvokeHandlersAfterFailure Ausführung von Handlern, wenn context.Fail die Eigenschaft aufgerufen wird, kurzschaltungen. InvokeHandlersAfterFailure standardmäßig auf true, in welchem Fall alle Handler aufgerufen werden.

Hinweis

Autorisierungshandler werden auch dann aufgerufen, wenn die Authentifizierung fehlschlägt.

Warum möchte ich mehrere Handler für eine Anforderung?

In Fällen, in denen die Auswertung auf einer OR-Basis erfolgen soll, implementieren Sie mehrere Handler für eine einzelne Anforderung. Microsoft verfügt z. B. über Türen, die nur mit Schlüsselkarten geöffnet werden. Wenn Sie Ihre Schlüsselkarte zu Hause verlassen, druckt der Rezeptionist einen temporären Aufkleber und öffnet die Tür für Sie. In diesem Szenario haben Sie eine einzelne Anforderung, BuildingEntry, aber mehrere Handler, die jeweils eine einzelne Anforderung untersuchen.

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

Stellen Sie sicher, dass beide Handler registriert sind. Wenn ein Handler erfolgreich ist, wenn eine Richtlinie die BuildingEntryRequirementBewertung auswertet, ist die Richtlinienbewertung erfolgreich.

Verwenden eines Func zum Erfüllen einer Richtlinie

Möglicherweise gibt es Situationen, in denen die Erfüllung einer Richtlinie einfach im Code ausgedrückt werden kann. Es ist möglich, eine Func<AuthorizationHandlerContext, bool> Richtlinie beim Konfigurieren Ihrer Richtlinie mit dem RequireAssertion Richtlinien-Generator zu bereitstellen.

Beispielsweise könnte der vorherige BadgeEntryHandler Vorgang wie folgt neu geschrieben werden:

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

Zugriff auf MVC-Anforderungskontext in Handlern

Die HandleRequirementAsync Methode, die Sie in einem Autorisierungshandler implementieren, weist zwei Parameter auf: eine AuthorizationHandlerContext und die TRequirement Behandlung. Frameworks, z. B. MVC, SignalR können der Eigenschaft AuthorizationHandlerContext ein beliebiges Objekt Resource hinzufügen, um zusätzliche Informationen zu übergeben.

Bei Verwendung des Endpunktroutings wird die Autorisierung in der Regel von der Autorisierungs-Middleware behandelt. In diesem Fall ist die Resource Eigenschaft eine Instanz von HttpContext. Der Kontext kann verwendet werden, um auf den aktuellen Endpunkt zuzugreifen, der verwendet werden kann, um die zugrunde liegende Ressource zu untersuchen, an die Sie routingen. Beispiel:

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

Bei herkömmlichem Routing oder wenn die Autorisierung als Teil des Autorisierungsfilters von MVC erfolgt, ist der Wert Resource einer AuthorizationFilterContext Instanz. Diese Eigenschaft bietet Zugriff auf HttpContext, RouteDataund alles andere, das von MVC und Razor Pages bereitgestellt wird.

Die Verwendung der Resource Eigenschaft ist frameworkspezifisch. Die Verwendung von Informationen in der Resource Eigenschaft beschränkt Ihre Autorisierungsrichtlinien auf bestimmte Frameworks. Wandeln Sie die Resource Eigenschaft mithilfe des is Schlüsselworts um, und bestätigen Sie dann, dass die Umwandlung erfolgreich war, um sicherzustellen, dass Der Code bei der InvalidCastException Ausführung auf anderen Frameworks nicht abstürzt:

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

Global erfordern, dass alle Benutzer authentifiziert werden

Informationen dazu, wie global die Authentifizierung aller Benutzer angefordert werden kann, finden Sie unter Authentifizierte Benutzer erforderlich.

Unterhalb der Abdeckungen verwenden rollenbasierte Autorisierung und anspruchsbasierte Autorisierung eine Anforderung, einen Anforderungshandler und eine vorkonfigurierte Richtlinie. Diese Bausteine unterstützen den Ausdruck von Autorisierungsauswertungen im Code. Das Ergebnis ist eine umfangreichere, wiederverwendbare, prüfbare Autorisierungsstruktur.

Eine Autorisierungsrichtlinie besteht aus mindestens einer Anforderung. Sie wird als Teil der Autorisierungsdienstkonfiguration in der Startup.ConfigureServices Methode registriert:

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

Im vorherigen Beispiel wird eine Richtlinie "AtLeast21" erstellt. Es verfügt über eine einzige Anforderung – die eines Mindestalters, das als Parameter für die Anforderung bereitgestellt wird.

IAuthorizationService

Der primäre Dienst, der bestimmt, ob die Autorisierung erfolgreich ist: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);
}

Der vorherige Code hebt die beiden Methoden des IAuthorizationService hervor.

IAuthorizationRequirement ist ein Markierungsdienst ohne Methoden und der Mechanismus zum Nachverfolgen, ob die Autorisierung erfolgreich ist.

Jeder IAuthorizationHandler ist dafür verantwortlich, zu überprüfen, ob anforderungen erfüllt sind:

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

Die AuthorizationHandlerContext Klasse verwendet den Handler, um zu kennzeichnen, ob anforderungen erfüllt wurden:

 context.Succeed(requirement)

Der folgende Code zeigt die vereinfachte (und mit Kommentaren versehene) Standardimplementierung des Autorisierungsdiensts:

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

Der folgende Code zeigt ein typisches 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();
}

Verwenden IAuthorizationService oder [Authorize(Policy = "Something")] für die Autorisierung.

Anwenden von Richtlinien auf MVC-Controller

Wenn Sie Seiten verwenden Razor , lesen Sie " Anwenden von Richtlinien auf Razor Seiten " in diesem Dokument.

Die Richtlinien werden auf den Controller angewendet, indem das [Authorize]-Attribut mit dem Richtliniennamen verwendet wird. Beispiel:

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

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

Anwenden von Richtlinien auf Razor Seiten

Richtlinien werden auf Razor Seiten angewendet, indem das [Authorize] Attribut mit dem Richtliniennamen verwendet wird. Beispiel:

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

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

Richtlinien können nicht auf Seitenhandlerebene Razor angewendet werden, sie müssen auf die Seite angewendet werden.

Richtlinien können mithilfe einer Autorisierungskonvention auf Razor Seiten angewendet werden.

Requirements (Anforderungen)

Eine Autorisierungsanforderung ist eine Sammlung von Datenparametern, mit denen eine Richtlinie den aktuellen Benutzerprinzipal auswerten kann. In unserer Richtlinie "AtLeast21" ist die Anforderung ein einzelner Parameter – das Mindestalter. Eine Anforderung implementiert IAuthorizationRequirement, die eine leere Markierungsschnittstelle ist. Eine parametrisierte Mindestaltersanforderung könnte wie folgt implementiert werden:

using Microsoft.AspNetCore.Authorization;

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

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

Wenn eine Autorisierungsrichtlinie mehrere Autorisierungsanforderungen enthält, müssen alle Anforderungen übergeben werden, damit die Richtlinienauswertung erfolgreich verläuft. Mit anderen Worten, mehrere Autorisierungsanforderungen, die einer einzelnen Autorisierungsrichtlinie hinzugefügt wurden, werden auf EINER AND-Basis behandelt.

Hinweis

Eine Anforderung muss nicht über Daten oder Eigenschaften verfügen.

Autorisierungshandler

Ein Autorisierungshandler ist für die Auswertung der Eigenschaften einer Anforderung verantwortlich. Der Autorisierungshandler wertet die Anforderungen anhand einer bereitgestellten Bereitstellung AuthorizationHandlerContext aus, um festzustellen, ob der Zugriff zulässig ist.

Eine Anforderung kann mehrere Handler aufweisen. Ein Handler erbt AuthorizationHandler<TRequirement>möglicherweise , wo TRequirement die Anforderung besteht, um behandelt zu werden. Alternativ kann ein Handler implementieren IAuthorizationHandler , um mehr als einen Anforderungstyp zu behandeln.

Verwenden eines Handlers für eine Anforderung

Das folgende Beispiel zeigt eine 1:1-Beziehung, in der ein Mindestaltershandler eine einzige Anforderung verwendet:

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

Der vorherige Code bestimmt, ob der aktuelle Benutzerprinzipal ein Geburtsdatum hat, das von einem bekannten und vertrauenswürdigen Aussteller ausgestellt wurde. Die Autorisierung kann nicht auftreten, wenn der Anspruch fehlt, in diesem Fall wird eine abgeschlossene Aufgabe zurückgegeben. Wenn ein Anspruch vorhanden ist, wird das Alter des Benutzers berechnet. Wenn der Benutzer das mindestalter erfüllt, das durch die Anforderung definiert ist, wird die Autorisierung als erfolgreich betrachtet. Wenn die Autorisierung erfolgreich ist, context.Succeed wird mit der erfüllten Anforderung als einziger Parameter aufgerufen.

Verwenden eines Handlers für mehrere Anforderungen

Das folgende Beispiel zeigt eine 1:n-Beziehung, in der ein Berechtigungshandler drei verschiedene Arten von Anforderungen verarbeiten kann:

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

Der vorherige Code durchläuft PendingRequirements– eine Eigenschaft, die Anforderungen enthält, die nicht als erfolgreich gekennzeichnet sind. Für eine ReadPermission Anforderung muss der Benutzer entweder Besitzer oder Sponsor sein, um auf die angeforderte Ressource zuzugreifen. Für eine EditPermission oder DeletePermission Anforderung muss der Benutzer ein Besitzer sein, um auf die angeforderte Ressource zuzugreifen.

Handlerregistrierung

Handler werden während der Konfiguration in der Dienstsammlung registriert. Beispiel:

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

Der vorherige Code registriert MinimumAgeHandler sich als Singleton durch Aufrufen services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();. Handler können mithilfe einer der integrierten Dienstlebensdauer registriert werden.

Was sollte ein Handler zurückgeben?

Beachten Sie, dass die Handle Methode im Handlerbeispiel keinen Wert zurückgibt. Wie ist ein Status von Erfolg oder Fehler angegeben?

  • Ein Handler gibt einen Erfolg durch Aufrufen context.Succeed(IAuthorizationRequirement requirement)an, wobei die Anforderung übergeben wird, die erfolgreich überprüft wurde.

  • Ein Handler muss im Allgemeinen keine Fehler behandeln, da andere Handler für dieselbe Anforderung möglicherweise erfolgreich sind.

  • Rufen Sie auf context.Fail, um einen Fehler zu gewährleisten, auch wenn andere Anforderungshandler erfolgreich sind.

Wenn ein Handler aufruft context.Succeed oder context.Failalle anderen Handler weiterhin aufgerufen werden. Auf diese Weise können Anforderungen Nebenwirkungen erzeugen, z. B. Protokollierung, die auch dann durchgeführt werden, wenn ein anderer Handler erfolgreich überprüft oder eine Anforderung fehlgeschlagen ist. Wenn die Eigenschaft auf false"Kurzschaltung" festgelegt ist, wird die InvokeHandlersAfterFailure Ausführung von Handlern aufgerufen context.Fail . InvokeHandlersAfterFailure standardmäßig auf true, in diesem Fall werden alle Handler aufgerufen.

Hinweis

Autorisierungshandler werden auch aufgerufen, wenn die Authentifizierung fehlschlägt.

Warum soll ich mehrere Handler für eine Anforderung benötigen?

In Fällen, in denen die Auswertung auf EINER OR-Basis erfolgen soll, implementieren Sie mehrere Handler für eine einzelne Anforderung. Beispielsweise verfügt Microsoft über Türen, die nur mit Schlüsselkarten geöffnet werden. Wenn Sie Ihre Schlüsselkarte zu Hause verlassen, druckt der Empfangsmitarbeiter einen temporären Aufkleber und öffnet die Tür für Sie. In diesem Szenario haben Sie eine einzelne Anforderung, BuildingEntry, aber mehrere Handler, die jeweils eine einzelne Anforderung untersuchen.

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

Stellen Sie sicher, dass beide Handler registriert sind. Wenn ein Handler erfolgreich ist, wenn eine Richtlinie die BuildingEntryRequirementBewertung auswertet, ist die Richtlinienauswertung erfolgreich.

Verwenden eines Func zum Erfüllen einer Richtlinie

Möglicherweise gibt es Situationen, in denen die Erfüllung einer Richtlinie einfach im Code ausgedrückt werden kann. Es ist möglich, eine Func<AuthorizationHandlerContext, bool> Beim Konfigurieren Ihrer Richtlinie mit dem RequireAssertion Richtlinien-Generator zu liefern.

Beispielsweise könnte die vorherige BadgeEntryHandler Neuschrift wie folgt erfolgen:

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

Zugreifen auf den MVC-Anforderungskontext in Handlern

Die HandleRequirementAsync Methode, die Sie in einem Autorisierungshandler implementieren, weist zwei Parameter auf: eine AuthorizationHandlerContext und die TRequirement Behandlung. Frameworks, z. B. MVC, können SignalR der Eigenschaft AuthorizationHandlerContext ein beliebiges Objekt Resource hinzufügen, um zusätzliche Informationen zu übergeben.

Bei Verwendung des Endpunktweiterleitungs wird die Autorisierung in der Regel von der Autorisierungs-Middleware behandelt. In diesem Fall ist die Resource Eigenschaft eine Instanz von Endpoint. Der Endpunkt kann verwendet werden, um die zugrunde liegende Ressource zu untersuchen, an die Sie routingen. Beispiel:

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

Der Endpunkt bietet keinen Zugriff auf den aktuellen HttpContext. Verwenden Sie beim Verwenden des Endpunktroutings IHttpContextAccessor den Zugriff HttpContext innerhalb eines Autorisierungshandlers. Weitere Informationen finden Sie unter Verwenden von HttpContext aus benutzerdefinierten Komponenten.

Bei herkömmlichem Routing oder wenn die Autorisierung als Teil des Autorisierungsfilters von MVC erfolgt, ist der Wert Resource einer AuthorizationFilterContext Instanz. Diese Eigenschaft bietet Zugriff auf HttpContextRouteData, und alles andere, was von MVC und Razor Pages bereitgestellt wird.

Die Verwendung der Resource Eigenschaft ist frameworkspezifisch. Die Verwendung von Informationen in der Resource Eigenschaft beschränkt Ihre Autorisierungsrichtlinien auf bestimmte Frameworks. Wandeln Sie die Resource Eigenschaft mithilfe des is Schlüsselworts um, und bestätigen Sie dann, dass die Umwandlung erfolgreich war, um sicherzustellen, dass Der Code bei der InvalidCastException Ausführung auf anderen Frameworks nicht abstürzt:

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

Global erfordern, dass alle Benutzer authentifiziert werden

Informationen dazu, wie global die Authentifizierung aller Benutzer angefordert werden kann, finden Sie unter Authentifizierte Benutzer erforderlich.