Nguon: Microsoft Learn · .NET 8.0

Ủy quyền dựa trên Claim trong ASP.NET Core MVC

Nguồn: Claim-based authorization in ASP.NET Core MVC

Khi một danh tính được tạo cho người dùng ứng dụng khi đăng nhập, identity provider (nhà cung cấp danh tính) có thể gán một hoặc nhiều claim cho danh tính của người dùng. Một claim là cặp name-value (tên-giá trị) đại diện cho chủ thể (một người dùng, ứng dụng hay dịch vụ, hoặc thiết bị/máy tính) là gì, không phải chủ thể có thể làm gì. Một claim có thể được ứng dụng đánh giá để xác định quyền truy cập vào dữ liệu và các tài nguyên bảo mật khác trong quá trình authorization và cũng có thể được sử dụng để đưa ra hoặc diễn đạt các quyết định xác thực về một chủ thể. Một danh tính có thể chứa nhiều claim với nhiều giá trị và có thể chứa nhiều claim cùng loại. Bài viết này giải thích cách thêm kiểm tra claim cho authorization trong ứng dụng ASP.NET Core.

Bài viết này sử dụng các ví dụ MVC và tập trung vào các kịch bản MVC. Để biết hướng dẫn về Blazor và Razor Pages, xem các tài nguyên sau:

Ứng dụng mẫu

Ứng dụng mẫu cho bài viết này là ứng dụng mẫu WebAll (GitHub repository dotnet/AspNetCore.Docs.Samples).

Thêm kiểm tra claim

Kiểm tra authorization dựa trên claim có tính khai báo và được áp dụng cho các controllers hoặc actions trong một controller.

Claim trong code chỉ định các claim mà người dùng hiện tại phải có, và tùy chọn giá trị claim phải giữ để truy cập tài nguyên được yêu cầu. Yêu cầu claim dựa trên policy. Nhà phát triển phải xây dựng và đăng ký policy thể hiện các yêu cầu claim.

Loại policy claim đơn giản nhất tìm kiếm sự hiện diện của một claim và không kiểm tra giá trị.

Xây dựng và đăng ký policy, sau đó gọi UseAuthorization (đặt lời gọi sau dòng gọi UseAuthentication). Việc đăng ký policy diễn ra như một phần của cấu hình Authorization service, thường trong file Program:

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthentication();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.Run();

Áp dụng policy bằng cách sử dụng thuộc tính Policy trên thuộc tính [Authorize] để chỉ định tên policy. Trong ví dụ sau, policy EmployeeOnly kiểm tra sự hiện diện của claim EmployeeNumber trong danh tính hiện tại:

csharp
[Authorize(Policy = "EmployeeOnly")]
public IActionResult VacationBalance()
{
    return View();
}

Thuộc tính [Authorize] có thể được áp dụng cho toàn bộ controller, trong trường hợp đó chỉ các danh tính khớp với policy mới được phép truy cập bất kỳ action nào trên controller:

csharp
[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public ActionResult VacationBalance()
    {
        return View();
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
        return View();
    }
}

Nếu bạn có controller được bảo vệ bởi thuộc tính [Authorize] nhưng muốn cho phép truy cập ẩn danh vào một action cụ thể, hãy áp dụng thuộc tính [AllowAnonymous]:

csharp
[Authorize(Policy = "EmployeeOnly")]
public class VacationController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public ActionResult VacationBalance()
    {
        return View();
    }

    [AllowAnonymous]
    public ActionResult VacationPolicy()
    {
        return View();
    }
}

Bạn có thể chỉ định danh sách các giá trị được phép khi tạo policy. Policy sau chỉ thông qua cho các nhân viên có số nhân viên là 1, 2, 3, 4 hoặc 5:

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("Founders", policy =>
        policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseAuthentication();
app.UseAuthorization();

app.MapDefaultControllerRoute();
app.MapRazorPages();

app.Run();

Thêm kiểm tra claim tổng quát

Nếu giá trị claim không phải là một giá trị đơn hoặc bạn cần logic đánh giá claim linh hoạt hơn, chẳng hạn như khớp mẫu (pattern matching), kiểm tra claim issuer, hoặc phân tích cú pháp các giá trị claim phức tạp, hãy sử dụng RequireAssertion với HasClaim. Ví dụ, policy sau yêu cầu claim email của người dùng kết thúc với một domain cụ thể:

csharp
builder.Services.AddAuthorizationBuilder()
    .AddPolicy("ContosoOnly", policy =>
        policy.RequireAssertion(context =>
            context.User.HasClaim(c =>
                c.Type == "email" &&
                c.Value.EndsWith("@contoso.com", StringComparison.OrdinalIgnoreCase))));

Để biết thêm thông tin, xem Policy-based authorization in ASP.NET Core.

Đánh giá nhiều policy

Nếu nhiều policy được áp dụng ở cấp độ controller và action, tất cả policy phải thông qua trước khi quyền truy cập được cấp:

csharp
[Authorize(Policy = "EmployeeOnly")]
public class SalaryController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    public IActionResult Payslip()
    {
        return View();
    }

    [Authorize(Policy = "HumanResources")]
    public IActionResult UpdateSalary()
    {
        return View();
    }
}

Trong ví dụ trên, bất kỳ danh tính nào thỏa mãn policy EmployeeOnly đều có thể truy cập action Payslip, vì policy đó được áp dụng trên controller. Tuy nhiên, để gọi action UpdateSalary, danh tính phải thỏa mãn cả hai policy EmployeeOnly HumanResources.

Nếu bạn muốn các policy phức tạp hơn, chẳng hạn như lấy claim ngày sinh, tính tuổi từ đó rồi kiểm tra tuổi có từ 21 trở lên, bạn cần viết custom policy handlers.

Phân biệt chữ hoa chữ thường trong Claim

Các giá trị claim được so sánh bằng StringComparison.Ordinal. Điều này có nghĩa Admin (chữ A viết hoa) và admin (chữ a viết thường) luôn được coi là các giá trị claim khác nhau, bất kể authentication handler nào đã tạo danh tính.

Riêng biệt, việc so sánh loại claim (được sử dụng để tìm claim theo loại của chúng, chẳng hạn như email) có thể phân biệt hoặc không phân biệt chữ hoa chữ thường tùy thuộc vào triển khai ClaimsIdentity. Với Microsoft.IdentityModel trong ASP.NET Core 8.0 trở lên (được sử dụng bởi AddJwtBearer, AddOpenIdConnect, AddWsFederation, và AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApi), CaseSensitiveClaimsIdentity được tạo ra trong quá trình xác thực token, sử dụng khớp loại claim phân biệt chữ hoa chữ thường.

ClaimsIdentity mặc định được cung cấp bởi .NET runtime (được sử dụng trong hầu hết các trường hợp, bao gồm tất cả các flow dựa trên cookie) vẫn sử dụng khớp loại claim không phân biệt chữ hoa chữ thường.

Trong thực tế, sự phân biệt này hiếm khi quan trọng khi loại claim được cấu hình một lần trong quá trình tạo danh tính và được khớp nhất quán. Điều này cũng áp dụng cho các role khi chúng được đại diện dưới dạng claim. Luôn sử dụng cách viết hoa nhất quán cho các giá trị claim và loại claim để tránh các vấn đề khó phát hiện.