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 — 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 yang disederhanakan (dan diannotasi dengan komentar) dari layanan otorisasi:
public async Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
object resource, IEnumerable<IAuthorizationRequirement> requirements)
{
// Create a tracking context from the authorization inputs.
var authContext = _contextFactory.CreateContext(requirements, user, resource);
// By default this returns an IEnumerable<IAuthorizationHandler> from DI.
var handlers = await _handlers.GetHandlersAsync(authContext);
// Invoke all handlers.
foreach (var handler in handlers)
{
await handler.HandleAsync(authContext);
}
// Check the context, by default success is when all requirements have been met.
return _evaluator.Evaluate(authContext);
}
Kode berikut menunjukkan konfigurasi layanan otorisasi umum:
// 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 Halaman, lihat bagian Terapkan kebijakan ke Razor Halaman .
Terapkan kebijakan ke pengontrol dengan menggunakan [Authorize]
atribut dengan nama kebijakan:
[Authorize(Policy = "AtLeast21")]
public class AtLeast21Controller : Controller
{
public IActionResult Index() => View();
}
Jika beberapa kebijakan diterapkan di tingkat pengontrol dan tindakan, semua kebijakan harus diteruskan sebelum akses diberikan:
[Authorize(Policy = "AtLeast21")]
public class AtLeast21Controller2 : Controller
{
[Authorize(Policy = "IdentificationValidated")]
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 berparameter 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
persyaratan untuk ditangani. Atau, handler dapat mengimplementasikan 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. Untuk persyaratan EditPermission
atau DeletePermission
, mereka harus menjadi pemilik untuk mengakses sumber daya yang diminta.
Pendaftaran handler
Daftarkan handler dalam kumpulan 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 IAuthorizationRequirement IAuthorizationHandler. Bundling ini menciptakan kopling 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 bahkan jika autentikasi gagal. Juga handler dapat dijalankan dalam urutan apa pun, jadi jangan bergantung pada mereka yang dipanggil dalam urutan tertentu.
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 home, 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 memenuhi 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 di handler
Metode HandleRequirementAsync
ini memiliki dua parameter: dan AuthorizationHandlerContext
yang TRequirement
sedang ditangani. Kerangka kerja seperti MVC atau SignalR gratis 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 pemeran telah berhasil memastikan kode Anda tidak mengalami crash 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 diautentikasi
Untuk informasi tentang cara mengharuskan semua pengguna diautentikasi secara global, lihat Memerlukan pengguna yang diautentikasi.
Otorisasi dengan sampel layanan eksternal
Kode sampel di AspNetCore.Docs.Samples menunjukkan cara menerapkan persyaratan otorisasi tambahan dengan layanan otorisasi eksternal. Proyek sampel Contoso.API
diamankan dengan Microsoft Azure AD. Pemeriksaan otorisasi tambahan dari Contoso.Security.API
proyek mengembalikan payload yang menjelaskan apakah Contoso.API
aplikasi klien dapat memanggil GetWeather
API.
Mengonfigurasi sampel
Buat pendaftaran aplikasi di penyewa ID Microsoft Entra Anda:
Tetapkan AppRole.
Di bawah izin API, tambahkan AppRole sebagai izin dan berikan persetujuan Admin. Perhatikan bahwa dalam penyiapan ini, pendaftaran aplikasi ini mewakili API dan klien yang memanggil API. Jika mau, Anda dapat membuat dua pendaftaran aplikasi. Jika Anda menggunakan penyiapan ini, pastikan untuk hanya melakukan izin API, tambahkan AppRole sebagai langkah izin hanya untuk klien. Hanya pendaftaran aplikasi klien yang memerlukan rahasia klien untuk dibuat.
Konfigurasikan
Contoso.API
proyek dengan pengaturan berikut:
{
"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": "*"
}
- Konfigurasikan
Contoso.Security.API
dengan pengaturan berikut:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"AllowedClients": [
"<Use the appropriate Client Id representing the Client calling the API>"
]
}
Buka file ContosoAPI.collection.json dan konfigurasikan lingkungan dengan yang berikut ini:
ClientId
: Id Klien dari pendaftaran aplikasi yang mewakili klien yang memanggil API.clientSecret
: Rahasia Klien dari pendaftaran aplikasi yang mewakili klien yang memanggil API.TenantId
: Id Penyewa dari properti AAD
Ekstrak perintah dari
ContosoAPI.collection.json
file dan gunakan untuk membuat perintah cURL untuk menguji aplikasi.Jalankan solusi dan gunakan cURL untuk memanggil API. Anda dapat menambahkan titik henti di
Contoso.Security.API.SecurityPolicyController
dan mengamati Id klien yang sedang diteruskan yang digunakan untuk menegaskan apakah diizinkan untuk Mendapatkan Cuaca.
Sumber Daya Tambahan:
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 yang disederhanakan (dan diannotasi dengan komentar) dari layanan otorisasi:
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 Halaman, lihat Menerapkan kebijakan ke Razor Halaman 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 berparameter 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. Untuk persyaratan EditPermission
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 IAuthorizationRequirement IAuthorizationHandler. Bundling ini menciptakan kopling 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 bahkan jika 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 home, 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 memenuhi kebijakan mudah diekspresikan dalam kode. Dimungkinkan untuk menyediakan Func<AuthorizationHandlerContext, bool>
saat mengonfigurasi kebijakan Anda dengan penyusun RequireAssertion
kebijakan.
Misalnya, 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 AuthorizationHandlerContext
TRequirement
yang Anda tangani. Kerangka kerja seperti MVC atau SignalR gratis 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 pemeran telah berhasil memastikan kode Anda tidak mengalami crash 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 diautentikasi
Untuk informasi tentang cara mengharuskan semua pengguna diautentikasi secara global, lihat Memerlukan pengguna yang diautentikasi.
ASP.NET Core