Poznámka
Přístup k této stránce vyžaduje autorizaci. Můžete se zkusit přihlásit nebo změnit adresáře.
Přístup k této stránce vyžaduje autorizaci. Můžete zkusit změnit adresáře.
Pod povrchem používají autorizace založená na rolích a autorizace založená na deklaracích požadavek, obslužný modul požadavku a předkonfigurovanou zásadu. Tyto stavební bloky podporují vyjadřování vyhodnocení autorizací 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ří pravidlo 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, která nemá žádné metody, ale obsahuje mechanismus pro sledování úspěšnosti autorizace.
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<IAuthorizationHandler> 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 sekci Použijte zásady pro 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 AtLeast21Controller2 : Controller
{
[Authorize(Policy = "IdentificationValidated")]
public IActionResult Index() => View();
}
Aplikovat zásady 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žijte zásady na koncové klienty
Použijte zásady na koncové body pomocí názvu zásady RequireAuthorization. Příklad:
app.MapGet("/helloworld", () => "Hello World!")
.RequireAuthorization("AtLeast21");
Požadavky
Požadavek na autorizaci je soubor datových parametrů, které může zásada použít k vyhodnocení aktuálního uživatelského objektu zabezpečení. 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é označovací rozhraní. 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í být všechny požadavky splněny, aby vyhodnocení zásady 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 zpracovatelů. 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, kde handler minimálního věku zpracovává jediný 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í uživatelský objekt tvrzení o datu narození, které vydal známý a důvěryhodný vystavitel. Autorizace nemůže být provedena, když chybí nárok, v takovém případě se vrátí dokončený úkol. Když je přítomen nárok, 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žijte zpracovatel pro více požadavků
Následující příklad ukazuje relaci 1:N, kde zpracovatel oprávnění můž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ě požadavku EditPermission
nebo DeletePermission
musí být vlastníkem, aby získali přístup k požadovanému prostředku.
Registrace obslužného programu
Během konfigurace zaregistrujte obslužné rutiny v kolekci služeb. Příklad:
builder.Services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
Předchozí kód 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ému objektu PassThroughAuthorizationHandler , který umožňuje, aby se požadavky zpracovávaly samy.
Podívejte se na implementaci AssertionRequirement 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í, volejte
context.Fail
, i když jsou jiné obslužné rutiny požadavků úspěšné.
Pokud obslužná rutina volá context.Succeed
nebo context.Fail
se 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. Když je nastavena na false
, vlastnost InvokeHandlersAfterFailure zkratuje provádění obslužných rutin při zavolání context.Fail
.
InvokeHandlersAfterFailure
je ve výchozím nastavení true
, v takovém případě jsou volány všechny obslužné rutiny.
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 nespoléhejte na to, že budou volány v konkrétním pořadí.
Proč bych pro požadavek chtěl více zpracovatelů?
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é programy zaregistrované. Pokud je některý z obslužných modulů úspěšný při vyhodnocení BuildingEntryRequirement
, vyhodnocení zásady proběhne úspěšně.
Použijte funkci k naplnění zásady
Mohou nastat situace, kdy je plnění zásady jednoduché vyjádřit v kódu. Při konfiguraci zásad s Func<AuthorizationHandlerContext, bool>
tvůrcem zásad je možné zadatRequireAssertion
.
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: AuthorizationHandlerContext
a TRequirement
, které jsou zpracovávány. Architektury, jako je MVC nebo SignalR, mohou volně přidat jakýkoli objekt do vlastnosti na Resource
na AuthorizationHandlerContext
pro předání dalších informací.
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ý lze použít k prozkoumání základního prostředku, 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 Resource
hodnota AuthorizationFilterContext instance. Tato vlastnost poskytuje přístup k HttpContext
, RouteData
a vše ostatní, co poskytuje MVC a Razor Pages.
Použití této vlastnosti je specifické pro rámec Resource
. Použití informací ve Resource
vlastnosti omezuje zásady autorizace na konkrétní rámce.
Resource
Přetypujte vlastnost pomocí is
klíčového slova a ověřte, že přetypování proběhlo úspěšně, aby se váš kód při spuštění na jiných platformách nesetkal s chybou 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
Vytvořte registraci aplikace v tenantu Microsoft Entra ID.
Přiřaďte jí AppRole.
V části Oprávnění rozhraní API přidejte AppRole jako oprávnění a udělte souhlas správce. 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í, ujistěte se, že provádíte pouze oprávnění API a přidejte AppRole jako krok oprávnění pouze pro klienta. Pouze registrace klientské aplikace vyžaduje vygenerování tajného klíče klienta.
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": "*"
}
- Nakonfigurujte
Contoso.Security.API
s následujícími nastaveními:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"AllowedClients": [
"<Use the appropriate Client Id representing the Client calling the API>"
]
}
Otevřete soubor ContosoAPI.collection.json 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 nájemce z vlastností platformy AAD
-
Extrahujte příkazy ze
ContosoAPI.collection.json
souboru a použijte je k vytvoření příkazů cURL k otestování aplikace.Spusťte řešení a k vyvolání rozhraní API použijte cURL . Do
Contoso.Security.API.SecurityPolicyController
můžete přidat zarážky a sledovat, jak se předává ID klienta, které slouží k ověření, zda je povoleno zjistit počasí.
Další materiály
Pod povrchem se používá autorizace založená na rolích a autorizace založená na deklaracích s požadavkem, obslužnou rutinou požadavků a předem nakonfigurovanou zásadou. Tyto stavební bloky podporují vyjadřování vyhodnocení autorizací 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ří pravidlo 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, která nemá žádné metody, ale obsahuje mechanismus pro sledování úspěšnosti autorizace.
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ý 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();
}
Použijte IAuthorizationService nebo [Authorize(Policy = "Something")]
pro autorizaci.
Aplikovat zásady 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();
}
Aplikovat zásady 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 soubor datových parametrů, které může zásada použít k vyhodnocení aktuálního uživatelského objektu zabezpečení. 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é označovací rozhraní. 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í být všechny požadavky splněny, aby vyhodnocení zásady 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 zpracovatelů. 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 k zpracování více typů požadavků.
Použití obslužné rutiny pro jeden požadavek
Následující příklad ukazuje vztah jedna ku jedné, ve kterém zpracovatel 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í uživatelský objekt tvrzení o datu narození, které vydal známý a důvěryhodný vystavitel. Autorizace nemůže být provedena, když chybí nárok, v takovém případě se vrátí dokončený úkol. Když je přítomen nárok, 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žijte zpracovatel pro více požadavků
Následující příklad ukazuje relaci 1:N, kde zpracovatel oprávnění můž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ě požadavku EditPermission
nebo DeletePermission
musí být uživatel vlastníkem, aby měl přístup k požadovanému prostředku.
Registrace obslužného programu
Během konfigurace se obslužné rutiny 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 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ému PassThroughAuthorizationHandler, který umožňuje požadavkům, aby se zpracovávaly samy.
Podívejte se na třída AssertionRequirement pro dobrý příklad, kde AssertionRequirement
je jak požadavek, tak 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í, volejte
context.Fail
, i když jsou jiné obslužné rutiny požadavků úspěšné.
Pokud obslužná rutina volá context.Succeed
nebo context.Fail
se 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. Když je nastavena na false
, vlastnost InvokeHandlersAfterFailure zkratuje provádění obslužných rutin při zavolání context.Fail
.
InvokeHandlersAfterFailure
je ve výchozím nastavení true
, v takovém případě jsou volány všechny obslužné rutiny.
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 zpracovatelů?
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é programy zaregistrované. Pokud je některý z obslužných modulů úspěšný při vyhodnocení BuildingEntryRequirement
, vyhodnocení zásady proběhne úspěšně.
Použijte funkci k naplnění zásady
Mohou nastat situace, kdy je plnění zásady jednoduché vyjádřit v kódu. Při konfiguraci vaší zásady pomocí tvůrce zásad Func<AuthorizationHandlerContext, bool>
je možné dodat RequireAssertion
.
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: AuthorizationHandlerContext
a TRequirement
, které zpracováváte. Architektury, jako je MVC nebo SignalR, mohou volně přidat jakýkoli objekt do vlastnosti na Resource
na AuthorizationHandlerContext
pro předání dalších informací.
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ý lze použít k prozkoumání základního prostředku, 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 Resource
hodnota AuthorizationFilterContext instance. Tato vlastnost poskytuje přístup k HttpContext
, RouteData
a vše ostatní, co poskytuje MVC a Razor Pages.
Použití této vlastnosti je specifické pro rámec Resource
. Použití informací ve Resource
vlastnosti omezuje zásady autorizace na konkrétní rámce.
Resource
Přetypujte vlastnost pomocí is
klíčového slova a ověřte, že přetypování proběhlo úspěšně, aby se váš kód při spuštění na jiných platformách nesetkal s chybou 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ů.