ASP.NET Core'da ilke tabanlı yetkilendirme

Kapakların altında rol tabanlı yetkilendirme ve talep tabanlı yetkilendirme bir gereksinim, gereksinim işleyicisi ve önceden yapılandırılmış bir ilke kullanır. Bu yapı taşları kodda yetkilendirme değerlendirmelerinin ifadesini destekler. Sonuç daha zengin, yeniden kullanılabilir, test edilebilir bir yetkilendirme yapısıdır.

Yetkilendirme ilkesi bir veya daha fazla gereksinimden oluşur. Yetkilendirme hizmeti yapılandırmasının bir parçası olarak uygulamanın Program.cs dosyasına kaydedin:

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

Yukarıdaki örnekte bir "AtLeast21" ilkesi oluşturulur. Tek bir gereksinimi vardır; minimum yaş, gereksinime parametre olarak sağlanır.

IAuthorizationService

Yetkilendirmenin başarılı olup olmadığını belirleyen birincil hizmet: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);
}

Yukarıdaki kod, IAuthorizationService'in iki yöntemini vurgular.

IAuthorizationRequirement yöntemi olmayan bir işaretçi hizmetidir ve yetkilendirmenin başarılı olup olmadığını izlemeye yönelik mekanizmadır.

Her IAuthorizationHandler biri gereksinimlerin karşılandığından sorumludur:

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

AuthorizationHandlerContext sınıfı, işleyicinin gereksinimlerin karşılanıp karşılanmadığını işaretlemek için kullandığı sınıftır:

 context.Succeed(requirement)

Aşağıdaki kod, yetkilendirme hizmetinin basitleştirilmiş (ve açıklamalarla ek açıklamalı) varsayılan uygulamasını gösterir:

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

Aşağıdaki kod tipik bir yetkilendirme hizmeti yapılandırmasını gösterir:

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

Yetkilendirme için , [Authorize(Policy = "Something")]veya RequireAuthorization("Something") kullanınIAuthorizationService.

MVC denetleyicilerine ilke uygulama

Sayfaları kullanan Razor uygulamalar için İlkeleri Sayfalara Razor uygulama bölümüne bakın.

İlke adıyla özniteliğini [Authorize] kullanarak denetleyicilere ilkeler uygulayın:

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

Denetleyici ve eylem düzeylerinde birden çok ilke uygulanırsa, erişim verilmeden önce tüm ilkelerin geçmesi gerekir:

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

Sayfalara Razor ilke uygulama

İlke adıyla özniteliğini [Authorize] kullanarak Sayfalar'a Razor ilkeler uygulayın. Örnek:

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

namespace AuthorizationPoliciesSample.Pages;

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

İlkeler Sayfa işleyici düzeyinde uygulanamıyorRazor, Sayfaya uygulanmalıdır.

İlkeler, yetkilendirme kuralı kullanılarak Sayfalar'a Razor da uygulanabilir.

Uç noktalara ilke uygulama

İlke adıyla kullanarak RequireAuthorization uç noktalara ilkeler uygulayın. Örnek:

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

Gereksinimler

Yetkilendirme gereksinimi, bir ilkenin geçerli kullanıcı sorumlusunu değerlendirmek için kullanabileceği veri parametreleri koleksiyonudur. "AtLeast21" ilkemizde gereksinim tek bir parametredir( en düşük yaş). Bir gereksinim, boş bir işaretçi arabirimi olan öğesini uygular IAuthorizationRequirement. Parametreli minimum yaş gereksinimi aşağıdaki gibi uygulanabilir:

using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Requirements;

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

    public int MinimumAge { get; }
}

Yetkilendirme ilkesi birden çok yetkilendirme gereksinimi içeriyorsa, ilke değerlendirmesinin başarılı olması için tüm gereksinimlerin geçmesi gerekir. Başka bir deyişle, tek bir yetkilendirme ilkesine eklenen birden çok yetkilendirme gereksinimi VE temelinde ele alınır.

Dekont

Bir gereksinimin veri veya özelliklere sahip olması gerekmez.

Yetkilendirme işleyicileri

Yetkilendirme işleyicisi, bir gereksinimin özelliklerinin değerlendirilmesinden sorumludur. Yetkilendirme işleyicisi, erişime izin verilip verilmediğini belirlemek için sağlanana AuthorizationHandlerContext göre gereksinimleri değerlendirir.

Bir gereksinim birden çok işleyiciye sahip olabilir. İşleyici devralabilir AuthorizationHandler<TRequirement>, burada TRequirement işlenmesi gereken bir gereksinimdir. Alternatif olarak, bir işleyici birden fazla gereksinim türünü işlemek için doğrudan uygulayabilir IAuthorizationHandler .

Bir gereksinim için işleyici kullanma

Aşağıdaki örnekte, en düşük yaş işleyicisinin tek bir gereksinimi işlediği bire bir ilişki gösterilmektedir:

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

Yukarıdaki kod, geçerli kullanıcı sorumlusunun bilinen ve güvenilen bir Veren tarafından verilmiş bir doğum talebi tarihi olup olmadığını belirler. Talep eksik olduğunda yetkilendirme gerçekleşemez ve bu durumda tamamlanmış bir görev döndürülür. Talep mevcut olduğunda kullanıcının yaşı hesaplanır. Kullanıcı gereksinim tarafından tanımlanan en düşük yaşı karşılıyorsa yetkilendirme başarılı kabul edilir. Yetkilendirme başarılı olduğunda, context.Succeed tek parametresi olarak karşılanan gereksinimle çağrılır.

Birden çok gereksinim için işleyici kullanma

Aşağıdaki örnekte, bir izin işleyicisinin üç farklı gereksinim türünü işleyebildiği bire çok ilişkisi gösterilmektedir:

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

Yukarıdaki kod geçiş yaparak PendingRequirementsgereksinimleri içeren ve başarılı olarak işaretlenmemiş bir özelliktir. ReadPermission Bir gereksinim için kullanıcının istenen kaynağa erişmek için sahip veya sponsor olması gerekir. EditPermission veya DeletePermission gereksinimi için, istenen kaynağa erişmek için sahip olmaları gerekir.

İşleyici kaydı

Yapılandırma sırasında hizmet koleksiyonuna işleyicileri kaydedin. Örnek:

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

Önceki kod tekil olarak kaydeder MinimumAgeHandler . İşleyiciler yerleşik hizmet ömründen herhangi biri kullanılarak kaydedilebilir.

Hem gereksinimi hem de işleyiciyi hem hem de IAuthorizationRequirementIAuthorizationHandleruygulayan tek bir sınıfa paketlemek mümkündür. Bu paketleme işleyici ile gereksinim arasında sıkı bir bağlantı oluşturur ve yalnızca basit gereksinimler ve işleyiciler için önerilir. Her iki arabirimi de uygulayan bir sınıf oluşturmak, gereksinimlerin kendilerini işlemesini sağlayan yerleşik PassThroughAuthorizationHandler nedeniyle işleyiciyi DI'ye kaydetme gereksinimini ortadan kaldırır.

Öğesinin hem bir gereksinim hem de tam olarak kendi içinde yer alan bir sınıfta işleyici olduğu AssertionRequirement iyi bir örnek için AssertionRequirement sınıfına bakın.

bir işleyici ne döndürmelidir?

İşleyici örneğindeki yönteminin değer döndürmediğini Handle unutmayın. Başarı veya başarısızlık durumu nasıl belirtilir?

  • İşleyici, başarıyla doğrulanmış gereksinimi geçirerek öğesini çağırarak context.Succeed(IAuthorizationRequirement requirement)başarıyı gösterir.

  • Aynı gereksinime yönelik diğer işleyiciler başarılı olabileceğinden, işleyicinin hataları genel olarak işlemesi gerekmez.

  • Başarısızlığı garanti etmek için, diğer gereksinim işleyicileri başarılı olsa bile öğesini çağırın context.Fail.

bir işleyici veya context.Failçağırırsacontext.Succeed, diğer tüm işleyiciler hala çağrılır. Bu, başka bir işleyici bir gereksinimi başarıyla doğrulasa veya başarısız olsa bile gerçekleşen günlüğe kaydetme gibi yan etkiler üretmeye olanak tanır. olarak falseInvokeHandlersAfterFailure ayarlandığında, özelliği çağrıldığında context.Fail işleyicilerin yürütülmesini kısa devreler. InvokeHandlersAfterFailure varsayılan değeridir trueve bu durumda tüm işleyiciler çağrılır.

Dekont

Kimlik doğrulaması başarısız olsa bile yetkilendirme işleyicileri çağrılır. Ayrıca işleyiciler herhangi bir sırada yürütülebilir, bu nedenle belirli bir sırada çağrılmalarına bağımlı değildir.

Neden bir gereksinim için birden çok işleyici isteyebilirim?

Değerlendirmenin VEYA temelinde olmasını istediğiniz durumlarda, tek bir gereksinim için birden çok işleyici uygulayın. Örneğin, Microsoft'un yalnızca anahtar kartlarıyla açılan kapıları vardır. Anahtar kartınızı evde bırakırsanız resepsiyonist geçici bir çıkartma yazdırır ve kapıyı sizin için açar. Bu senaryoda, tek bir gereksiniminiz (BuildingEntry) ancak her biri tek bir gereksinimi inceleyerek birden çok işleyiciye sahip olursunuz.

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

her iki işleyicinin de kayıtlı olduğundan emin olun. bir ilke değerini değerlendirdiğinde işleyicilerden BuildingEntryRequirementbiri başarılı olursa, ilke değerlendirmesi başarılı olur.

İlkeyi yerine getirmek için func kullanma

bir ilkeyi yerine getirmenin kodla ifade edilmesi kolay olduğu durumlar olabilir. İlke Func<AuthorizationHandlerContext, bool> oluşturucusu ile RequireAssertion bir ilke yapılandırırken sağlamak mümkündür.

Örneğin, önceki BadgeEntryHandler aşağıdaki gibi yeniden yazılabilir:

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

İşleyicilerde MVC isteği bağlamı erişimi

yöntemi iki HandleRequirementAsync parametreye sahiptir: an AuthorizationHandlerContext ve TRequirement işleniyor. MVC gibi çerçeveler veya SignalR ek bilgi geçirmek için Resource üzerinde AuthorizationHandlerContext özelliğine herhangi bir nesne eklemek ücretsizdir.

Uç nokta yönlendirme kullanılırken yetkilendirme genellikle Yetkilendirme Ara Yazılımı tarafından işlenir. Bu durumda özelliği Resource bir örneğidir HttpContext. Bağlam geçerli uç noktaya erişmek için kullanılabilir ve bu da yönlendirme yaptığınız temel kaynağı araştırmak için kullanılabilir. Örnek:

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

Geleneksel yönlendirmede veya MVC yetkilendirme filtresinin bir parçası olarak yetkilendirme gerçekleştiğinde değeri Resource bir AuthorizationFilterContext örnektir. Bu özellik, , RouteDatave MVC ve Razor Pages tarafından sağlanan diğer her şeye erişim HttpContextsağlar.

özelliğinin kullanımı çerçeveye Resource özgüdür. özelliğindeki Resource bilgileri kullanmak yetkilendirme ilkelerinizi belirli çerçevelerle sınırlar. anahtar sözcüğünü kullanarak özelliğini yayınlayın Resourceis ve ardından diğer çerçevelerde çalıştırıldığında kodunuzun bir InvalidCastException ile kilitlenmediğinden emin olmak için atamanın başarılı olduğunu onaylayın:

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

Genel olarak tüm kullanıcıların kimliğinin doğrulanması gerekir

Genel olarak tüm kullanıcıların kimliğinin doğrulanmış olmasını gerektirme hakkında bilgi için bkz . Kimliği doğrulanmış kullanıcılar gerektirme.

Dış hizmet örneğiyle yetkilendirme

AspNetCore.Docs.Samples üzerindeki örnek kod, bir dış yetkilendirme hizmetiyle ek yetkilendirme gereksinimlerini nasıl uygulayacaklarını gösterir. Örnek Contoso.API projenin güvenliği Azure AD ile sağlanır. Projeden yapılan ek yetkilendirme denetimi, istemci uygulamasının Contoso.Security.APIContoso.API API'yi çağırıp çağıramayacağını GetWeather açıklayan bir yük döndürür.

Örneği yapılandırma

  1. Azure Active Directory (Azure AD) kiracınızda uygulama kaydı oluşturun:
  • Bir AppRole atayın.
  • API izinleri altında AppRole'ı izin olarak ekleyin ve Yönetici onay verin. Bu kurulumda, bu uygulama kaydının hem API'yi hem de API'yi çağıran istemciyi temsil ettiğini unutmayın. İsterseniz iki uygulama kaydı oluşturabilirsiniz. Bu kurulumu kullanıyorsanız, yalnızca API izinlerini gerçekleştirdiğinizden emin olun, appRole'u yalnızca istemci için izin adımı olarak ekleyin. Yalnızca istemci uygulama kaydı için bir istemci gizli dizisi oluşturulması gerekir.
  1. Contoso.API Projeyi aşağıdaki ayarlarla yapılandırın:
{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "Domain": "<Tenant name from AAD properties>.onmicrosoft.com">,
    "TenantId": "<Tenant Id from AAD properties>",
    "ClientId": "<Client Id from App Registration representing the API>"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*"
}
  1. Aşağıdaki ayarlarla yapılandırın Contoso.Security.API :
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "AllowedHosts": "*",
  "AllowedClients": [
    "<Use the appropriate Client Id representing the Client calling the API>"
  ]
}
  1. ContosoAPI.postman_collection.json dosyasını Postman'a aktarın ve aşağıdakilerle bir ortam yapılandırın:

    • ClientId: API'yi çağıran istemciyi temsil eden uygulama kaydından istemci kimliği.
    • clientSecret: API'yi çağıran istemciyi temsil eden uygulama kaydından İstemci Gizli Dizisi.
    • TenantId: AAD özelliklerinden kiracı kimliği
  2. Çözümü çalıştırın ve API'yi çağırmak için Postman'i kullanın. öğesine Contoso.Security.API.SecurityPolicyController kesme noktaları ekleyebilir ve hava durumunu alma izninin verilip verilmeyeceğini onaylamada kullanılan istemci kimliğinin geçirildiğini gözlemleyebilirsiniz.

Ek kaynaklar

Kapakların altında rol tabanlı yetkilendirme ve talep tabanlı yetkilendirme bir gereksinim, gereksinim işleyicisi ve önceden yapılandırılmış bir ilke kullanır. Bu yapı taşları kodda yetkilendirme değerlendirmelerinin ifadesini destekler. Sonuç daha zengin, yeniden kullanılabilir, test edilebilir bir yetkilendirme yapısıdır.

Yetkilendirme ilkesi bir veya daha fazla gereksinimden oluşur. Aşağıdaki yöntemde yetkilendirme hizmeti yapılandırmasının Startup.ConfigureServices bir parçası olarak kaydedilir:

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

Yukarıdaki örnekte bir "AtLeast21" ilkesi oluşturulur. Tek bir gereksinimi vardır; minimum yaş, gereksinime parametre olarak sağlanır.

IAuthorizationService

Yetkilendirmenin başarılı olup olmadığını belirleyen birincil hizmet: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);
}

Yukarıdaki kod, IAuthorizationService'in iki yöntemini vurgular.

IAuthorizationRequirement yöntemi olmayan bir işaretçi hizmetidir ve yetkilendirmenin başarılı olup olmadığını izlemeye yönelik mekanizmadır.

Her IAuthorizationHandler biri gereksinimlerin karşılandığından sorumludur:

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

AuthorizationHandlerContext sınıfı, işleyicinin gereksinimlerin karşılanıp karşılanmadığını işaretlemek için kullandığı sınıftır:

 context.Succeed(requirement)

Aşağıdaki kod, yetkilendirme hizmetinin basitleştirilmiş (ve açıklamalarla ek açıklamalı) varsayılan uygulamasını gösterir:

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

Aşağıdaki kod tipik ConfigureServicesbir gösterir:

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

Yetkilendirme için veya [Authorize(Policy = "Something")] kullanınIAuthorizationService.

MVC denetleyicisine ilke uygulama

Pages kullanıyorsanızRazor, bu belgedeki Sayfalara ilke uygulama konusuna Razor bakın.

İlkeler, ilke adıyla özniteliği kullanılarak [Authorize] denetleyicilere uygulanır. Örnek:

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

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

Sayfalara Razor ilke uygulama

İlkeler, ilke adıyla özniteliği kullanılarak [Authorize] Sayfalar'a Razor uygulanır. Örnek:

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

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

İlkeler Sayfa işleyici düzeyinde uygulanamıyorRazor, Sayfaya uygulanmalıdır.

İlkeler, yetkilendirme kuralı kullanılarak Sayfalar'a Razor uygulanabilir.

Gereksinimler

Yetkilendirme gereksinimi, bir ilkenin geçerli kullanıcı sorumlusunu değerlendirmek için kullanabileceği veri parametreleri koleksiyonudur. "AtLeast21" ilkemizde gereksinim tek bir parametredir( en düşük yaş). Bir gereksinim, boş bir işaretçi arabirimi olan öğesini uygular IAuthorizationRequirement. Parametreli minimum yaş gereksinimi aşağıdaki gibi uygulanabilir:

using Microsoft.AspNetCore.Authorization;

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

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

Yetkilendirme ilkesi birden çok yetkilendirme gereksinimi içeriyorsa, ilke değerlendirmesinin başarılı olması için tüm gereksinimlerin geçmesi gerekir. Başka bir deyişle, tek bir yetkilendirme ilkesine eklenen birden çok yetkilendirme gereksinimi VE temelinde ele alınır.

Dekont

Bir gereksinimin veri veya özelliklere sahip olması gerekmez.

Yetkilendirme işleyicileri

Yetkilendirme işleyicisi, bir gereksinimin özelliklerinin değerlendirilmesinden sorumludur. Yetkilendirme işleyicisi, erişime izin verilip verilmediğini belirlemek için sağlanana AuthorizationHandlerContext göre gereksinimleri değerlendirir.

Bir gereksinim birden çok işleyiciye sahip olabilir. İşleyici devralabilir AuthorizationHandler<TRequirement>, burada TRequirement işlenmesi gereken bir gereksinimdir. Alternatif olarak, bir işleyici birden fazla gereksinim türünü işlemek için uygulayabilir IAuthorizationHandler .

Bir gereksinim için işleyici kullanma

Aşağıdaki örnekte, en düşük yaş işleyicisinin tek bir gereksinimi kullandığı bire bir ilişki gösterilmektedir:

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

Yukarıdaki kod, geçerli kullanıcı sorumlusunun bilinen ve güvenilen bir Veren tarafından verilmiş bir doğum talebi tarihi olup olmadığını belirler. Talep eksik olduğunda yetkilendirme gerçekleşemez ve bu durumda tamamlanmış bir görev döndürülür. Talep mevcut olduğunda kullanıcının yaşı hesaplanır. Kullanıcı gereksinim tarafından tanımlanan en düşük yaşı karşılıyorsa yetkilendirme başarılı kabul edilir. Yetkilendirme başarılı olduğunda, context.Succeed tek parametresi olarak karşılanan gereksinimle çağrılır.

Birden çok gereksinim için işleyici kullanma

Aşağıdaki örnekte, bir izin işleyicisinin üç farklı gereksinim türünü işleyebildiği bire çok ilişkisi gösterilmektedir:

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

Yukarıdaki kod geçiş yaparak PendingRequirementsgereksinimleri içeren ve başarılı olarak işaretlenmemiş bir özelliktir. ReadPermission Bir gereksinim için kullanıcının istenen kaynağa erişmek için sahip veya sponsor olması gerekir. EditPermission Veya DeletePermission gereksinimi için kullanıcının istenen kaynağa erişebilmesi için sahip olması gerekir.

İşleyici kaydı

İşleyiciler yapılandırma sırasında hizmet koleksiyonuna kaydedilir. Örnek:

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

Yukarıdaki kod, çağırarak services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();tekil olarak kaydederMinimumAgeHandler. İşleyiciler yerleşik hizmet ömründen herhangi biri kullanılarak kaydedilebilir.

Hem gereksinimi hem de işleyiciyi hem de IAuthorizationRequirementIAuthorizationHandleruygulayan tek bir sınıfta paketlemek mümkündür. Bu paketleme işleyici ile gereksinim arasında sıkı bir bağlantı oluşturur ve yalnızca basit gereksinimler ve işleyiciler için önerilir. Her iki arabirimi de uygulayan bir sınıf oluşturmak, gereksinimlerin kendilerini işlemesini sağlayan yerleşik PassThroughAuthorizationHandler nedeniyle işleyiciyi DI'ye kaydetme gereksinimini ortadan kaldırır.

Öğesinin hem bir gereksinim hem de tam olarak kendi içinde yer alan bir sınıfta işleyici olduğu AssertionRequirement iyi bir örnek için AssertionRequirement sınıfına bakın.

bir işleyici ne döndürmelidir?

İşleyici örneğindeki yönteminin değer döndürmediğini Handle unutmayın. Başarı veya başarısızlık durumu nasıl belirtilir?

  • İşleyici, başarıyla doğrulanmış gereksinimi geçirerek öğesini çağırarak context.Succeed(IAuthorizationRequirement requirement)başarıyı gösterir.

  • Aynı gereksinime yönelik diğer işleyiciler başarılı olabileceğinden, işleyicinin hataları genel olarak işlemesi gerekmez.

  • Başarısızlığı garanti etmek için, diğer gereksinim işleyicileri başarılı olsa bile öğesini çağırın context.Fail.

bir işleyici veya context.Failçağırırsacontext.Succeed, diğer tüm işleyiciler hala çağrılır. Bu, başka bir işleyici bir gereksinimi başarıyla doğrulasa veya başarısız olsa bile gerçekleşen günlüğe kaydetme gibi yan etkiler üretmeye olanak tanır. olarak falseInvokeHandlersAfterFailure ayarlandığında, özelliği çağrıldığında context.Fail işleyicilerin yürütülmesini kısa devreler. InvokeHandlersAfterFailure varsayılan değeridir trueve bu durumda tüm işleyiciler çağrılır.

Dekont

Kimlik doğrulaması başarısız olsa bile yetkilendirme işleyicileri çağrılır.

Neden bir gereksinim için birden çok işleyici isteyebilirim?

Değerlendirmenin VEYA temelinde olmasını istediğiniz durumlarda, tek bir gereksinim için birden çok işleyici uygulayın. Örneğin, Microsoft'un yalnızca anahtar kartlarıyla açılan kapıları vardır. Anahtar kartınızı evde bırakırsanız resepsiyonist geçici bir çıkartma yazdırır ve kapıyı sizin için açar. Bu senaryoda, tek bir gereksiniminiz (BuildingEntry) ancak her biri tek bir gereksinimi inceleyerek birden çok işleyiciye sahip olursunuz.

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

her iki işleyicinin de kayıtlı olduğundan emin olun. bir ilke değerini değerlendirdiğinde işleyicilerden BuildingEntryRequirementbiri başarılı olursa, ilke değerlendirmesi başarılı olur.

İlkeyi yerine getirmek için func kullanma

bir ilkeyi yerine getirmenin kodla ifade edilmesi kolay olduğu durumlar olabilir. İlke oluşturucu ile RequireAssertion ilkenizi yapılandırırken bir Func<AuthorizationHandlerContext, bool> sağlamak mümkündür.

Örneğin, önceki BadgeEntryHandler aşağıdaki gibi yeniden yazılabilir:

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

İşleyicilerde MVC isteği bağlamı erişimi

Yetkilendirme HandleRequirementAsync işleyicisinde uyguladığınız yöntemin iki parametresi vardır: an AuthorizationHandlerContext ve işlemekte TRequirement olduğunuz. MVC gibi çerçeveler veya SignalR ek bilgi geçirmek için Resource üzerinde AuthorizationHandlerContext özelliğine herhangi bir nesne eklemek ücretsizdir.

Uç nokta yönlendirme kullanılırken yetkilendirme genellikle Yetkilendirme Ara Yazılımı tarafından işlenir. Bu durumda özelliği Resource bir örneğidir HttpContext. Bağlam geçerli uç noktaya erişmek için kullanılabilir ve bu da yönlendirme yaptığınız temel kaynağı araştırmak için kullanılabilir. Örnek:

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

Geleneksel yönlendirmede veya MVC yetkilendirme filtresinin bir parçası olarak yetkilendirme gerçekleştiğinde değeri Resource bir AuthorizationFilterContext örnektir. Bu özellik, , RouteDatave MVC ve Razor Pages tarafından sağlanan diğer her şeye erişim HttpContextsağlar.

özelliğinin kullanımı çerçeveye Resource özgüdür. özelliğindeki Resource bilgileri kullanmak yetkilendirme ilkelerinizi belirli çerçevelerle sınırlar. anahtar sözcüğünü kullanarak özelliğini yayınlayın Resourceis ve ardından diğer çerçevelerde çalıştırıldığında kodunuzun bir InvalidCastException ile kilitlenmediğinden emin olmak için atamanın başarılı olduğunu onaylayın:

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

Genel olarak tüm kullanıcıların kimliğinin doğrulanması gerekir

Genel olarak tüm kullanıcıların kimliğinin doğrulanmış olmasını gerektirme hakkında bilgi için bkz . Kimliği doğrulanmış kullanıcılar gerektirme.