Otorisasi berbasis kebijakan di ASP.NET Core

Di bawah sampul, otorisasi berbasis peran dan otorisasi berbasis klaim menggunakan persyaratan, penangan persyaratan, dan kebijakan yang telah dikonfigurasi sebelumnya. Blok penyusun ini mendukung ekspresi evaluasi otorisasi dalam kode. Hasilnya adalah struktur otorisasi yang lebih kaya, dapat digunakan kembali, dan dapat diuji.

Kebijakan otorisasi terdiri dari satu atau beberapa persyaratan. Daftarkan sebagai bagian dari konfigurasi layanan otorisasi, dalam file aplikasi Program.cs :

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

Dalam contoh sebelumnya, kebijakan "AtLeast21" dibuat. Ini memiliki satu persyaratan —usia minimum, yang disediakan sebagai parameter untuk persyaratan.

IAuthorizationService

Layanan utama yang menentukan apakah otorisasi berhasil adalah 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);
}

Kode sebelumnya menyoroti dua metode IAuthorizationService.

IAuthorizationRequirement adalah layanan penanda tanpa metode, dan mekanisme untuk melacak apakah otorisasi berhasil.

Masing-masing IAuthorizationHandler bertanggung jawab untuk memeriksa apakah persyaratan terpenuhi:

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

Kelas AuthorizationHandlerContext adalah apa yang digunakan handler untuk menandai apakah persyaratan telah terpenuhi:

 context.Succeed(requirement)

Kode berikut menunjukkan implementasi default layanan otorisasi yang disederhanakan (dan dianotasikan dengan komentar):

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

Kode berikut menunjukkan konfigurasi layanan otorisasi yang khas:

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

Gunakan IAuthorizationService, [Authorize(Policy = "Something")], atau RequireAuthorization("Something") untuk otorisasi.

Menerapkan kebijakan ke pengontrol MVC

Untuk aplikasi yang menggunakan Razor Pages, lihat bagian Menerapkan kebijakan ke Razor Halaman .

Terapkan kebijakan ke pengontrol dengan menggunakan [Authorize] atribut dengan nama kebijakan. Contohnya:

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

namespace AuthorizationPoliciesSample.Controllers;

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

Menerapkan kebijakan ke Razor Halaman

Terapkan kebijakan ke Razor Pages dengan menggunakan [Authorize] atribut dengan nama kebijakan. Contohnya:

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

namespace AuthorizationPoliciesSample.Pages;

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

Kebijakan tidak dapat diterapkan di Razor tingkat handler Halaman, kebijakan tersebut harus diterapkan ke Halaman.

Kebijakan juga dapat diterapkan ke Razor Pages dengan menggunakan konvensi otorisasi.

Menerapkan kebijakan ke titik akhir

Terapkan kebijakan ke titik akhir dengan menggunakan RequireAuthorization dengan nama kebijakan. Contohnya:

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

Persyaratan

Persyaratan otorisasi adalah kumpulan parameter data yang dapat digunakan kebijakan untuk mengevaluasi prinsipal pengguna saat ini. Dalam kebijakan "AtLeast21" kami, persyaratannya adalah parameter tunggal—usia minimum. Persyaratan mengimplementasikan IAuthorizationRequirement, yang merupakan antarmuka penanda kosong. Persyaratan usia minimum yang diparameterkan dapat diimplementasikan sebagai berikut:

using Microsoft.AspNetCore.Authorization;

namespace AuthorizationPoliciesSample.Policies.Requirements;

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

    public int MinimumAge { get; }
}

Jika kebijakan otorisasi berisi beberapa persyaratan otorisasi, semua persyaratan harus diteruskan agar evaluasi kebijakan berhasil. Dengan kata lain, beberapa persyaratan otorisasi yang ditambahkan ke satu kebijakan otorisasi diperlakukan berdasarkan AND .

Catatan

Persyaratan tidak perlu memiliki data atau properti.

Penangan otorisasi

Handler otorisasi bertanggung jawab atas evaluasi properti persyaratan. Handler otorisasi mengevaluasi persyaratan terhadap yang disediakan AuthorizationHandlerContext untuk menentukan apakah akses diizinkan.

Persyaratan dapat memiliki beberapa handler. Handler dapat mewarisi AuthorizationHandler<TRequirement>, di mana TRequirement adalah persyaratan untuk ditangani. Atau, handler dapat menerapkan IAuthorizationHandler secara langsung untuk menangani lebih dari satu jenis persyaratan.

Menggunakan handler untuk satu persyaratan

Contoh berikut menunjukkan hubungan satu-ke-satu di mana penangan usia minimum menangani satu persyaratan:

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

Kode sebelumnya menentukan apakah prinsipal pengguna saat ini memiliki klaim tanggal lahir yang telah dikeluarkan oleh Penerbit yang diketahui dan tepercaya. Otorisasi tidak dapat terjadi ketika klaim hilang, dalam hal ini tugas yang selesai dikembalikan. Saat klaim ada, usia pengguna dihitung. Jika pengguna memenuhi usia minimum yang ditentukan oleh persyaratan, otorisasi dianggap berhasil. Ketika otorisasi berhasil, context.Succeed dipanggil dengan persyaratan yang terpenuhi sebagai parameter satu-satunya.

Menggunakan handler untuk beberapa persyaratan

Contoh berikut menunjukkan hubungan satu-ke-banyak di mana penangan izin dapat menangani tiga jenis persyaratan yang berbeda:

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

Kode sebelumnya melintasi PendingRequirements—properti yang berisi persyaratan yang tidak ditandai sebagai berhasil. ReadPermission Untuk persyaratan, pengguna harus menjadi pemilik atau sponsor untuk mengakses sumber daya yang diminta. EditPermission Untuk persyaratan atau DeletePermission , mereka harus menjadi pemilik untuk mengakses sumber daya yang diminta.

Pendaftaran handler

Daftarkan handler dalam koleksi layanan selama konfigurasi. Contohnya:

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

Kode sebelumnya mendaftar MinimumAgeHandler sebagai singleton. Handler dapat didaftarkan menggunakan salah satu masa pakai layanan bawaan.

Dimungkinkan untuk menggabungkan persyaratan dan handler ke dalam satu kelas yang mengimplementasikan dan IAuthorizationRequirementIAuthorizationHandler. Bundling ini menciptakan konupling yang ketat antara handler dan persyaratan dan hanya direkomendasikan untuk persyaratan dan handler sederhana. Membuat kelas yang mengimplementasikan kedua antarmuka menghilangkan kebutuhan untuk mendaftarkan handler di DI karena PassThroughAuthorizationHandler bawaan yang memungkinkan persyaratan untuk menangani diri mereka sendiri.

Lihat kelas AssertionRequirement untuk contoh yang baik di mana AssertionRequirement adalah persyaratan dan handler di kelas yang sepenuhnya mandiri.

Apa yang harus dikembalikan oleh handler?

Perhatikan bahwa Handle metode dalam contoh handler tidak mengembalikan nilai. Bagaimana status keberhasilan atau kegagalan ditunjukkan?

  • Handler menunjukkan keberhasilan dengan memanggil context.Succeed(IAuthorizationRequirement requirement), meneruskan persyaratan yang telah berhasil divalidasi.

  • Handler tidak perlu menangani kegagalan umumnya, karena handler lain untuk persyaratan yang sama mungkin berhasil.

  • Untuk menjamin kegagalan, bahkan jika penangan persyaratan lain berhasil, panggil context.Fail.

Jika handler memanggil context.Succeed atau context.Fail, semua handler lain masih dipanggil. Ini memungkinkan persyaratan untuk menghasilkan efek samping, seperti pengelogan, yang terjadi bahkan jika handler lain telah berhasil memvalidasi atau gagal persyaratan. Ketika diatur ke false, InvokeHandlersAfterFailure properti short-circuits eksekusi handler ketika context.Fail dipanggil. InvokeHandlersAfterFailure default ke true, dalam hal ini semua handler dipanggil.

Catatan

Handler otorisasi dipanggil meskipun autentikasi gagal.

Mengapa saya ingin beberapa handler untuk persyaratan?

Dalam kasus di mana Anda ingin evaluasi berdasarkan OR , terapkan beberapa handler untuk satu persyaratan. Misalnya, Microsoft memiliki pintu yang hanya terbuka dengan kartu kunci. Jika Anda meninggalkan kartu kunci Anda di rumah, resepsionis mencetak stiker sementara dan membuka pintu untuk Anda. Dalam skenario ini, Anda akan memiliki satu persyaratan, BuildingEntry, tetapi beberapa handler, masing-masing memeriksa satu persyaratan.

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

Pastikan kedua handler terdaftar. Jika salah satu handler berhasil ketika kebijakan mengevaluasi BuildingEntryRequirement, evaluasi kebijakan berhasil.

Menggunakan func untuk memenuhi kebijakan

Mungkin ada situasi di mana pemenuhan kebijakan mudah diekspresikan dalam kode. Dimungkinkan untuk menyediakan Func<AuthorizationHandlerContext, bool> saat mengonfigurasi kebijakan dengan penyusun RequireAssertion kebijakan.

Misalnya, sebelumnya BadgeEntryHandler dapat ditulis ulang sebagai berikut:

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

Mengakses konteks permintaan MVC dalam handler

Metode HandleRequirementAsync ini memiliki dua parameter: dan AuthorizationHandlerContext yang TRequirement sedang ditangani. Kerangka kerja seperti MVC atau SignalR bebas menambahkan objek apa pun ke Resource properti untuk meneruskan AuthorizationHandlerContext informasi tambahan.

Saat menggunakan perutean titik akhir, otorisasi biasanya ditangani oleh Middleware Otorisasi. Dalam hal ini, Resource properti adalah instans dari HttpContext. Konteks dapat digunakan untuk mengakses titik akhir saat ini, yang dapat digunakan untuk memeriksa sumber daya yang mendasar tempat Anda merutekan. Contohnya:

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

Dengan perutean tradisional, atau ketika otorisasi terjadi sebagai bagian dari filter otorisasi MVC, nilainya Resource adalah AuthorizationFilterContext instans. Properti ini menyediakan akses ke HttpContext, RouteData, dan segala sesuatu yang disediakan oleh MVC dan Razor Pages.

Penggunaan Resource properti ini khusus untuk kerangka kerja. Menggunakan informasi dalam Resource properti membatasi kebijakan otorisasi Anda ke kerangka kerja tertentu. Transmisikan Resource properti menggunakan is kata kunci , lalu konfirmasikan bahwa transmisi telah berhasil memastikan kode Anda tidak crash dengan InvalidCastException saat dijalankan pada kerangka kerja lain:

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

Secara global mengharuskan semua pengguna untuk diautentikasi

Untuk informasi tentang cara mengharuskan semua pengguna diautentikasi secara global, lihat Memerlukan pengguna yang diautentikasi.

Di bawah sampul, otorisasi berbasis peran dan otorisasi berbasis klaim menggunakan persyaratan, penangan persyaratan, dan kebijakan yang telah dikonfigurasi sebelumnya. Blok penyusun ini mendukung ekspresi evaluasi otorisasi dalam kode. Hasilnya adalah struktur otorisasi yang lebih kaya, dapat digunakan kembali, dan dapat diuji.

Kebijakan otorisasi terdiri dari satu atau beberapa persyaratan. Ini terdaftar sebagai bagian dari konfigurasi layanan otorisasi, dalam Startup.ConfigureServices metode :

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

Dalam contoh sebelumnya, kebijakan "AtLeast21" dibuat. Ini memiliki satu persyaratan — dengan usia minimum, yang disediakan sebagai parameter untuk persyaratan.

IAuthorizationService

Layanan utama yang menentukan apakah otorisasi berhasil adalah 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);
}

Kode sebelumnya menyoroti dua metode IAuthorizationService.

IAuthorizationRequirement adalah layanan penanda tanpa metode, dan mekanisme untuk melacak apakah otorisasi berhasil.

Masing-masing IAuthorizationHandler bertanggung jawab untuk memeriksa apakah persyaratan terpenuhi:

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

Kelas AuthorizationHandlerContext adalah apa yang digunakan handler untuk menandai apakah persyaratan telah terpenuhi:

 context.Succeed(requirement)

Kode berikut menunjukkan implementasi default layanan otorisasi yang disederhanakan (dan diannotasi dengan komentar):

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

Kode berikut menunjukkan tipikal 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();
}

Gunakan IAuthorizationService atau [Authorize(Policy = "Something")] untuk otorisasi.

Menerapkan kebijakan ke pengontrol MVC

Jika Anda menggunakan Razor Pages, lihat Menerapkan kebijakan ke Razor Pages dalam dokumen ini.

Kebijakan diterapkan pada pengontrol dengan menggunakan atribut [Authorize] dengan nama kebijakan. Contohnya:

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

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

Menerapkan kebijakan ke Razor Halaman

Kebijakan diterapkan ke Razor Pages dengan menggunakan [Authorize] atribut dengan nama kebijakan. Contohnya:

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

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

Kebijakan tidak dapat diterapkan di Razor tingkat handler Halaman, kebijakan tersebut harus diterapkan ke Halaman.

Kebijakan dapat diterapkan ke Razor Pages dengan menggunakan konvensi otorisasi.

Persyaratan

Persyaratan otorisasi adalah kumpulan parameter data yang dapat digunakan kebijakan untuk mengevaluasi prinsipal pengguna saat ini. Dalam kebijakan "AtLeast21" kami, persyaratannya adalah parameter tunggal—usia minimum. Persyaratan mengimplementasikan IAuthorizationRequirement, yang merupakan antarmuka penanda kosong. Persyaratan usia minimum yang diparameterkan dapat diimplementasikan sebagai berikut:

using Microsoft.AspNetCore.Authorization;

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

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

Jika kebijakan otorisasi berisi beberapa persyaratan otorisasi, semua persyaratan harus diteruskan agar evaluasi kebijakan berhasil. Dengan kata lain, beberapa persyaratan otorisasi yang ditambahkan ke satu kebijakan otorisasi diperlakukan berdasarkan AND .

Catatan

Persyaratan tidak perlu memiliki data atau properti.

Penangan otorisasi

Handler otorisasi bertanggung jawab atas evaluasi properti persyaratan. Handler otorisasi mengevaluasi persyaratan terhadap yang disediakan AuthorizationHandlerContext untuk menentukan apakah akses diizinkan.

Persyaratan dapat memiliki beberapa handler. Handler dapat mewarisi AuthorizationHandler<TRequirement>, di mana TRequirement persyaratan untuk ditangani. Atau, handler dapat menerapkan IAuthorizationHandler untuk menangani lebih dari satu jenis persyaratan.

Menggunakan handler untuk satu persyaratan

Contoh berikut menunjukkan hubungan satu-ke-satu di mana penangan usia minimum menggunakan satu persyaratan:

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

Kode sebelumnya menentukan apakah prinsipal pengguna saat ini memiliki klaim tanggal lahir yang telah dikeluarkan oleh Penerbit yang diketahui dan tepercaya. Otorisasi tidak dapat terjadi ketika klaim hilang, dalam hal ini tugas yang selesai dikembalikan. Saat klaim ada, usia pengguna dihitung. Jika pengguna memenuhi usia minimum yang ditentukan oleh persyaratan, otorisasi dianggap berhasil. Ketika otorisasi berhasil, context.Succeed dipanggil dengan persyaratan yang terpenuhi sebagai parameter satu-satunya.

Menggunakan handler untuk beberapa persyaratan

Contoh berikut menunjukkan hubungan satu-ke-banyak di mana penangan izin dapat menangani tiga jenis persyaratan yang berbeda:

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

Kode sebelumnya melintasi PendingRequirements—properti yang berisi persyaratan yang tidak ditandai sebagai berhasil. ReadPermission Untuk persyaratan, pengguna harus menjadi pemilik atau sponsor untuk mengakses sumber daya yang diminta. EditPermission Untuk persyaratan atau DeletePermission , pengguna harus menjadi pemilik untuk mengakses sumber daya yang diminta.

Pendaftaran handler

Handler terdaftar dalam kumpulan layanan selama konfigurasi. Contohnya:

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

Kode sebelumnya mendaftar MinimumAgeHandler sebagai singleton dengan memanggil services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();. Handler dapat didaftarkan menggunakan salah satu masa pakai layanan bawaan.

Dimungkinkan untuk menggabungkan persyaratan dan handler dalam satu kelas yang mengimplementasikan dan IAuthorizationRequirementIAuthorizationHandler. Bundling ini menciptakan konupling yang ketat antara handler dan persyaratan dan hanya direkomendasikan untuk persyaratan dan handler sederhana. Membuat kelas yang mengimplementasikan kedua antarmuka menghilangkan kebutuhan untuk mendaftarkan handler di DI karena PassThroughAuthorizationHandler bawaan yang memungkinkan persyaratan untuk menangani dirinya sendiri.

Lihat kelas AssertionRequirement untuk contoh yang baik di mana AssertionRequirement adalah persyaratan dan handler di kelas yang sepenuhnya mandiri.

Apa yang harus dikembalikan oleh handler?

Perhatikan bahwa Handle metode dalam contoh handler tidak mengembalikan nilai. Bagaimana status keberhasilan atau kegagalan ditunjukkan?

  • Handler menunjukkan keberhasilan dengan memanggil context.Succeed(IAuthorizationRequirement requirement), melewati persyaratan yang telah berhasil divalidasi.

  • Handler tidak perlu menangani kegagalan umumnya, karena handler lain untuk persyaratan yang sama mungkin berhasil.

  • Untuk menjamin kegagalan, bahkan jika penangan persyaratan lain berhasil, panggil context.Fail.

Jika handler memanggil context.Succeed atau context.Fail, semua handler lain masih dipanggil. Ini memungkinkan persyaratan untuk menghasilkan efek samping, seperti pengelogan, yang terjadi bahkan jika handler lain telah berhasil memvalidasi atau gagal persyaratan. Ketika diatur ke false, InvokeHandlersAfterFailure properti short-circuits eksekusi handler ketika context.Fail dipanggil. InvokeHandlersAfterFailure default ke true, dalam hal ini semua handler dipanggil.

Catatan

Handler otorisasi dipanggil meskipun autentikasi gagal.

Mengapa saya ingin beberapa handler untuk persyaratan?

Dalam kasus di mana Anda ingin evaluasi berada berdasarkan OR , terapkan beberapa handler untuk satu persyaratan. Misalnya, Microsoft memiliki pintu yang hanya terbuka dengan kartu kunci. Jika Anda meninggalkan kartu kunci di rumah, resepsionis mencetak stiker sementara dan membuka pintu untuk Anda. Dalam skenario ini, Anda akan memiliki satu persyaratan, BuildingEntry, tetapi beberapa handler, masing-masing memeriksa satu persyaratan.

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

Pastikan kedua handler terdaftar. Jika salah satu handler berhasil ketika kebijakan mengevaluasi BuildingEntryRequirement, evaluasi kebijakan berhasil.

Menggunakan func untuk memenuhi kebijakan

Mungkin ada situasi di mana pemenuhan kebijakan mudah diekspresikan dalam kode. Dimungkinkan untuk menyediakan Func<AuthorizationHandlerContext, bool> saat mengonfigurasi kebijakan Anda dengan penyusun RequireAssertion kebijakan.

Misalnya, yang sebelumnya BadgeEntryHandler dapat ditulis ulang sebagai berikut:

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

Mengakses konteks permintaan MVC di handler

Metode HandleRequirementAsync yang Anda terapkan dalam handler otorisasi memiliki dua parameter: dan AuthorizationHandlerContextTRequirement yang Anda tangani. Kerangka kerja seperti MVC atau SignalR bebas untuk menambahkan objek apa pun ke Resource properti pada untuk meneruskan AuthorizationHandlerContext informasi tambahan.

Saat menggunakan perutean titik akhir, otorisasi biasanya ditangani oleh Middleware Otorisasi. Dalam hal ini, Resource properti adalah instans dari HttpContext. Konteks dapat digunakan untuk mengakses titik akhir saat ini, yang dapat digunakan untuk memeriksa sumber daya yang mendasar tempat Anda merutekan. Contohnya:

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

Dengan perutean tradisional, atau ketika otorisasi terjadi sebagai bagian dari filter otorisasi MVC, nilainya Resource adalah AuthorizationFilterContext instans. Properti ini menyediakan akses ke HttpContext, RouteData, dan segala sesuatu yang lain yang disediakan oleh MVC dan Razor Pages.

Penggunaan Resource properti ini khusus untuk kerangka kerja. Menggunakan informasi dalam Resource properti membatasi kebijakan otorisasi Anda ke kerangka kerja tertentu. Transmisikan Resource properti menggunakan is kata kunci , lalu konfirmasikan bahwa transmisi telah berhasil memastikan kode Anda tidak crash dengan InvalidCastException saat dijalankan pada kerangka kerja lain:

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

Secara global mengharuskan semua pengguna untuk diautentikasi

Untuk informasi tentang cara mengharuskan semua pengguna diautentikasi secara global, lihat Memerlukan pengguna yang diautentikasi.

Di bawah sampul, otorisasi berbasis peran dan otorisasi berbasis klaim menggunakan persyaratan, penangan persyaratan, dan kebijakan yang telah dikonfigurasi sebelumnya. Blok penyusun ini mendukung ekspresi evaluasi otorisasi dalam kode. Hasilnya adalah struktur otorisasi yang lebih kaya, dapat digunakan kembali, dan dapat diuji.

Kebijakan otorisasi terdiri dari satu atau beberapa persyaratan. Ini terdaftar sebagai bagian dari konfigurasi layanan otorisasi, dalam Startup.ConfigureServices metode :

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

Dalam contoh sebelumnya, kebijakan "AtLeast21" dibuat. Ini memiliki satu persyaratan — dengan usia minimum, yang disediakan sebagai parameter untuk persyaratan.

IAuthorizationService

Layanan utama yang menentukan apakah otorisasi berhasil adalah 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);
}

Kode sebelumnya menyoroti dua metode IAuthorizationService.

IAuthorizationRequirement adalah layanan penanda tanpa metode, dan mekanisme untuk melacak apakah otorisasi berhasil.

Masing-masing IAuthorizationHandler bertanggung jawab untuk memeriksa apakah persyaratan terpenuhi:

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

Kelas AuthorizationHandlerContext adalah apa yang digunakan handler untuk menandai apakah persyaratan telah terpenuhi:

 context.Succeed(requirement)

Kode berikut menunjukkan implementasi default layanan otorisasi yang disederhanakan (dan diannotasi dengan komentar):

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

Kode berikut menunjukkan tipikal 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();
}

Gunakan IAuthorizationService atau [Authorize(Policy = "Something")] untuk otorisasi.

Menerapkan kebijakan ke pengontrol MVC

Jika Anda menggunakan Razor Pages, lihat Menerapkan kebijakan ke Razor Pages dalam dokumen ini.

Kebijakan diterapkan pada pengontrol dengan menggunakan atribut [Authorize] dengan nama kebijakan. Contohnya:

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

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

Menerapkan kebijakan ke Razor Halaman

Kebijakan diterapkan ke Razor Pages dengan menggunakan [Authorize] atribut dengan nama kebijakan. Contohnya:

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

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

Kebijakan tidak dapat diterapkan di Razor tingkat handler Halaman, kebijakan tersebut harus diterapkan ke Halaman.

Kebijakan dapat diterapkan ke Razor Pages dengan menggunakan konvensi otorisasi.

Persyaratan

Persyaratan otorisasi adalah kumpulan parameter data yang dapat digunakan kebijakan untuk mengevaluasi prinsipal pengguna saat ini. Dalam kebijakan "AtLeast21" kami, persyaratannya adalah parameter tunggal—usia minimum. Persyaratan mengimplementasikan IAuthorizationRequirement, yang merupakan antarmuka penanda kosong. Persyaratan usia minimum yang diparameterkan dapat diimplementasikan sebagai berikut:

using Microsoft.AspNetCore.Authorization;

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

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

Jika kebijakan otorisasi berisi beberapa persyaratan otorisasi, semua persyaratan harus diteruskan agar evaluasi kebijakan berhasil. Dengan kata lain, beberapa persyaratan otorisasi yang ditambahkan ke satu kebijakan otorisasi diperlakukan berdasarkan AND .

Catatan

Persyaratan tidak perlu memiliki data atau properti.

Penangan otorisasi

Handler otorisasi bertanggung jawab atas evaluasi properti persyaratan. Handler otorisasi mengevaluasi persyaratan terhadap yang disediakan AuthorizationHandlerContext untuk menentukan apakah akses diizinkan.

Persyaratan dapat memiliki beberapa handler. Handler dapat mewarisi AuthorizationHandler<TRequirement>, di mana TRequirement persyaratan untuk ditangani. Atau, handler dapat menerapkan IAuthorizationHandler untuk menangani lebih dari satu jenis persyaratan.

Menggunakan handler untuk satu persyaratan

Contoh berikut menunjukkan hubungan satu-ke-satu di mana penangan usia minimum menggunakan satu persyaratan:

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

Kode sebelumnya menentukan apakah prinsipal pengguna saat ini memiliki klaim tanggal lahir yang telah dikeluarkan oleh Penerbit yang diketahui dan tepercaya. Otorisasi tidak dapat terjadi ketika klaim hilang, dalam hal ini tugas yang selesai dikembalikan. Saat klaim ada, usia pengguna dihitung. Jika pengguna memenuhi usia minimum yang ditentukan oleh persyaratan, otorisasi dianggap berhasil. Ketika otorisasi berhasil, context.Succeed dipanggil dengan persyaratan yang terpenuhi sebagai parameter satu-satunya.

Menggunakan handler untuk beberapa persyaratan

Contoh berikut menunjukkan hubungan satu-ke-banyak di mana penangan izin dapat menangani tiga jenis persyaratan yang berbeda:

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

Kode sebelumnya melintasi PendingRequirements—properti yang berisi persyaratan yang tidak ditandai sebagai berhasil. ReadPermission Untuk persyaratan, pengguna harus menjadi pemilik atau sponsor untuk mengakses sumber daya yang diminta. EditPermission Untuk persyaratan atau DeletePermission , pengguna harus menjadi pemilik untuk mengakses sumber daya yang diminta.

Pendaftaran handler

Handler terdaftar dalam kumpulan layanan selama konfigurasi. Contohnya:

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

Kode sebelumnya mendaftar MinimumAgeHandler sebagai singleton dengan memanggil services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();. Handler dapat didaftarkan menggunakan salah satu masa pakai layanan bawaan.

Apa yang harus dikembalikan oleh handler?

Perhatikan bahwa Handle metode dalam contoh handler tidak mengembalikan nilai. Bagaimana status keberhasilan atau kegagalan ditunjukkan?

  • Handler menunjukkan keberhasilan dengan memanggil context.Succeed(IAuthorizationRequirement requirement), melewati persyaratan yang telah berhasil divalidasi.

  • Handler tidak perlu menangani kegagalan umumnya, karena handler lain untuk persyaratan yang sama mungkin berhasil.

  • Untuk menjamin kegagalan, bahkan jika penangan persyaratan lain berhasil, panggil context.Fail.

Jika handler memanggil context.Succeed atau context.Fail, semua handler lain masih dipanggil. Ini memungkinkan persyaratan untuk menghasilkan efek samping, seperti pengelogan, yang terjadi bahkan jika handler lain telah berhasil memvalidasi atau gagal persyaratan. Ketika diatur ke false, InvokeHandlersAfterFailure properti short-circuits eksekusi handler ketika context.Fail dipanggil. InvokeHandlersAfterFailure default ke true, dalam hal ini semua handler dipanggil.

Catatan

Handler otorisasi dipanggil meskipun autentikasi gagal.

Mengapa saya ingin beberapa handler untuk persyaratan?

Dalam kasus di mana Anda ingin evaluasi berada berdasarkan OR , terapkan beberapa handler untuk satu persyaratan. Misalnya, Microsoft memiliki pintu yang hanya terbuka dengan kartu kunci. Jika Anda meninggalkan kartu kunci di rumah, resepsionis mencetak stiker sementara dan membuka pintu untuk Anda. Dalam skenario ini, Anda akan memiliki satu persyaratan, BuildingEntry, tetapi beberapa handler, masing-masing memeriksa satu persyaratan.

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

Pastikan kedua handler terdaftar. Jika salah satu handler berhasil ketika kebijakan mengevaluasi BuildingEntryRequirement, evaluasi kebijakan berhasil.

Menggunakan func untuk memenuhi kebijakan

Mungkin ada situasi di mana pemenuhan kebijakan mudah diekspresikan dalam kode. Dimungkinkan untuk menyediakan Func<AuthorizationHandlerContext, bool> saat mengonfigurasi kebijakan Anda dengan penyusun RequireAssertion kebijakan.

Misalnya, yang sebelumnya BadgeEntryHandler dapat ditulis ulang sebagai berikut:

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

Mengakses konteks permintaan MVC di handler

Metode HandleRequirementAsync yang Anda terapkan dalam handler otorisasi memiliki dua parameter: dan AuthorizationHandlerContextTRequirement yang Anda tangani. Kerangka kerja seperti MVC atau SignalR bebas untuk menambahkan objek apa pun ke Resource properti pada untuk meneruskan AuthorizationHandlerContext informasi tambahan.

Saat menggunakan perutean titik akhir, otorisasi biasanya ditangani oleh Middleware Otorisasi. Dalam hal ini, Resource properti adalah instans dari Endpoint. Titik akhir dapat digunakan untuk memeriksa sumber daya yang mendasar tempat Anda merutekan. Contohnya:

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

Titik akhir tidak menyediakan akses ke saat ini HttpContext. Saat menggunakan perutean titik akhir, gunakan IHttpContextAccessor untuk mengakses HttpContext di dalam handler otorisasi. Untuk informasi selengkapnya, lihat Menggunakan HttpContext dari komponen kustom.

Dengan perutean tradisional, atau ketika otorisasi terjadi sebagai bagian dari filter otorisasi MVC, nilainya Resource adalah AuthorizationFilterContext instans. Properti ini menyediakan akses ke HttpContext, RouteData, dan segala sesuatu yang lain yang disediakan oleh MVC dan Razor Pages.

Penggunaan Resource properti ini khusus untuk kerangka kerja. Menggunakan informasi dalam Resource properti membatasi kebijakan otorisasi Anda ke kerangka kerja tertentu. Transmisikan Resource properti menggunakan is kata kunci , lalu konfirmasikan bahwa transmisi telah berhasil memastikan kode Anda tidak crash dengan InvalidCastException saat dijalankan pada kerangka kerja lain:

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

Secara global mengharuskan semua pengguna untuk diautentikasi

Untuk informasi tentang cara mengharuskan semua pengguna diautentikasi secara global, lihat Memerlukan pengguna yang diautentikasi.