Share via


ASP.NET Core 中的資源型授權

授權方法取決於資源。 例如,只有文件的作者有權更新文件。 因此,必須先從資料存放區擷取文件,才能進行授權評估。

屬性評估會在資料繫結之前,以及在載入文件的頁面處理常式或動作之前發生。 基於這些原因,具有 [Authorize] 屬性的宣告式授權會不足。 相反地,您可以叫用自訂授權方法,也就是稱為命令式授權的樣式。

檢視或下載範例程式碼 (如何下載)。

使用受授權保護的使用者資料建立 ASP.NET Core 應用程式,其中包含使用資源型授權的範例應用程式。

使用命令式授權

授權會實作為 IAuthorizationService 服務,並在應用程式啟動時在服務集合中註冊。 該服務透過對頁面處理常式或動作的相依性插入來提供。

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService 有兩個 AuthorizeAsync 方法來多載:一個接受資源和原則名稱,另一個接受資源和要評估的需求清單。

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

在下列範例中,要保護的資源會載入至自訂 Document 物件。 叫用 AuthorizeAsync 多載,以判斷是否允許目前使用者編輯提供的文件。 自訂的「EditPolicy」授權原則會納入決策中。 如需建立授權原則的詳細資訊,請參閱自訂原則型授權

注意

下列程式碼範例假設驗證已執行並設定 User 屬性。

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

撰寫資源型處理常式

撰寫資源型授權的處理常式與撰寫一般需求處理常式並無太大不同。 建立自訂需求類別,並實作需求處理常式類別。 如需建立需求類別的詳細資訊,請參閱需求

處理常式類別會同時指定需求和資源類型。 例如,使用 SameAuthorRequirementDocument 資源的處理常式如下:

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

在上述範例中,假設 SameAuthorRequirement 是更泛型 SpecificAuthorRequirement 類別的特殊案例。 類別 SpecificAuthorRequirement (未顯示) 包含代表作者名稱的 Name 屬性。 屬性 Name 可以設定為目前的使用者。

Program.cs 中註冊需求和處理常式:

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

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
builder.Services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
builder.Services.AddScoped<IDocumentRepository, DocumentRepository>();

作業需求

如果您要根據 CRUD 的結果做出決策 (建立、讀取、更新、刪除) 作業,請使用 OperationAuthorizationRequirement 協助程式類別。 此類別可讓您撰寫單一處理程式,而不是每個作業類型的個別類別。 若要使用它,請提供一些作業名稱:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

處理常式實作方式如下,使用 OperationAuthorizationRequirement 需求和資源 Document

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

上述處理常式會使用資源、使用者的身分識別和需求的 Name 屬性來驗證作業。

操作資源處理常式的挑戰與禁止

本節說明如何處理挑戰和禁止動作結果,以及挑戰和禁止有何差異。

如要呼叫作業性資源處理常式,請在頁面處理常式或動作中叫用 AuthorizeAsync 時指定作業。 下列範例會判斷是否允許已驗證的使用者檢視所提供的文件。

注意

下列程式碼範例假設驗證已執行並設定 User 屬性。

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

如果授權成功,則會傳回檢視文件的頁面。 如果授權失敗,但使用者有經過驗證,則傳回 ForbidResult 會通知任何驗證中介軟體授權失敗。 必須執行驗證時,會傳回 ChallengeResult。 針對互動式瀏覽器用戶端,將使用者重新導向至登入頁面可能較適合。

授權方法取決於資源。 例如,只有文件的作者有權更新文件。 因此,必須先從資料存放區擷取文件,才能進行授權評估。

屬性評估會在資料繫結之前,以及在載入文件的頁面處理常式或動作之前發生。 基於這些原因,具有 [Authorize] 屬性的宣告式授權會不足。 相反地,您可以叫用自訂授權方法,也就是稱為命令式授權的樣式。

檢視或下載範例程式碼 (如何下載)。

使用受授權保護的使用者資料建立 ASP.NET Core 應用程式,其中包含使用資源型授權的範例應用程式。

使用命令式授權

授權會實作為 IAuthorizationService 服務,並在 Startup 類別內的服務集合中註冊。 該服務透過對頁面處理常式或動作的相依性插入來提供。

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService 有兩個 AuthorizeAsync 方法來多載:一個接受資源和原則名稱,另一個接受資源和要評估的需求清單。

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

在下列範例中,要保護的資源會載入至自訂 Document 物件。 叫用 AuthorizeAsync 多載,以判斷是否允許目前使用者編輯提供的文件。 自訂的「EditPolicy」授權原則會納入決策中。 如需建立授權原則的詳細資訊,請參閱自訂原則型授權

注意

下列程式碼範例假設驗證已執行並設定 User 屬性。

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

撰寫資源型處理常式

撰寫資源型授權的處理常式與撰寫一般需求處理常式並無太大不同。 建立自訂需求類別,並實作需求處理常式類別。 如需建立需求類別的詳細資訊,請參閱需求

處理常式類別會同時指定需求和資源類型。 例如,使用 SameAuthorRequirementDocument 資源的處理常式如下:

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

在上述範例中,假設 SameAuthorRequirement 是更泛型 SpecificAuthorRequirement 類別的特殊案例。 類別 SpecificAuthorRequirement (未顯示) 包含代表作者名稱的 Name 屬性。 屬性 Name 可以設定為目前的使用者。

Startup.ConfigureServices 中註冊需求和處理常式:

services.AddControllersWithViews();
services.AddRazorPages();

services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();

作業需求

如果您要根據 CRUD 的結果做出決策 (建立、讀取、更新、刪除) 作業,請使用 OperationAuthorizationRequirement 協助程式類別。 此類別可讓您撰寫單一處理程式,而不是每個作業類型的個別類別。 若要使用它,請提供一些作業名稱:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

處理常式實作方式如下,使用 OperationAuthorizationRequirement 需求和資源 Document

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

上述處理常式會使用資源、使用者的身分識別和需求的 Name 屬性來驗證作業。

操作資源處理常式的挑戰與禁止

本節說明如何處理挑戰和禁止動作結果,以及挑戰和禁止有何差異。

如要呼叫作業性資源處理常式,請在頁面處理常式或動作中叫用 AuthorizeAsync 時指定作業。 下列範例會判斷是否允許已驗證的使用者檢視所提供的文件。

注意

下列程式碼範例假設驗證已執行並設定 User 屬性。

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

如果授權成功,則會傳回檢視文件的頁面。 如果授權失敗,但使用者有經過驗證,則傳回 ForbidResult 會通知任何驗證中介軟體授權失敗。 必須執行驗證時,會傳回 ChallengeResult。 針對互動式瀏覽器用戶端,將使用者重新導向至登入頁面可能較適合。

授權方法取決於資源。 例如,只有文件的作者有權更新文件。 因此,必須先從資料存放區擷取文件,才能進行授權評估。

屬性評估會在資料繫結之前,以及在載入文件的頁面處理常式或動作之前發生。 基於這些原因,具有 [Authorize] 屬性的宣告式授權會不足。 相反地,您可以叫用自訂授權方法,也就是稱為命令式授權的樣式。

檢視或下載範例程式碼 (如何下載)。

使用受授權保護的使用者資料建立 ASP.NET Core 應用程式,其中包含使用資源型授權的範例應用程式。

使用命令式授權

授權會實作為 IAuthorizationService 服務,並在 Startup 類別內的服務集合中註冊。 該服務透過對頁面處理常式或動作的相依性插入來提供。

public class DocumentController : Controller
{
    private readonly IAuthorizationService _authorizationService;
    private readonly IDocumentRepository _documentRepository;

    public DocumentController(IAuthorizationService authorizationService,
                              IDocumentRepository documentRepository)
    {
        _authorizationService = authorizationService;
        _documentRepository = documentRepository;
    }

IAuthorizationService 有兩個 AuthorizeAsync 方法來多載:一個接受資源和原則名稱,另一個接受資源和要評估的需求清單。

Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          IEnumerable<IAuthorizationRequirement> requirements);
Task<AuthorizationResult> AuthorizeAsync(ClaimsPrincipal user,
                          object resource,
                          string policyName);

在下列範例中,要保護的資源會載入至自訂 Document 物件。 叫用 AuthorizeAsync 多載,以判斷是否允許目前使用者編輯提供的文件。 自訂的「EditPolicy」授權原則會納入決策中。 如需建立授權原則的詳細資訊,請參閱自訂原則型授權

注意

下列程式碼範例假設驗證已執行並設定 User 屬性。

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, "EditPolicy");

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

撰寫資源型處理常式

撰寫資源型授權的處理常式與撰寫一般需求處理常式並無太大不同。 建立自訂需求類別,並實作需求處理常式類別。 如需建立需求類別的詳細資訊,請參閱需求

處理常式類別會同時指定需求和資源類型。 例如,使用 SameAuthorRequirementDocument 資源的處理常式如下:

public class DocumentAuthorizationHandler : 
    AuthorizationHandler<SameAuthorRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   SameAuthorRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

public class SameAuthorRequirement : IAuthorizationRequirement { }

在上述範例中,假設 SameAuthorRequirement 是更泛型 SpecificAuthorRequirement 類別的特殊案例。 類別 SpecificAuthorRequirement (未顯示) 包含代表作者名稱的 Name 屬性。 屬性 Name 可以設定為目前的使用者。

Startup.ConfigureServices 中註冊需求和處理常式:

services.AddMvc();

services.AddAuthorization(options =>
{
    options.AddPolicy("EditPolicy", policy =>
        policy.Requirements.Add(new SameAuthorRequirement()));
});

services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationHandler>();
services.AddSingleton<IAuthorizationHandler, DocumentAuthorizationCrudHandler>();
services.AddScoped<IDocumentRepository, DocumentRepository>();

作業需求

如果您要根據 CRUD 的結果做出決策 (建立、讀取、更新、刪除) 作業,請使用 OperationAuthorizationRequirement 協助程式類別。 此類別可讓您撰寫單一處理程式,而不是每個作業類型的個別類別。 若要使用它,請提供一些作業名稱:

public static class Operations
{
    public static OperationAuthorizationRequirement Create =
        new OperationAuthorizationRequirement { Name = nameof(Create) };
    public static OperationAuthorizationRequirement Read =
        new OperationAuthorizationRequirement { Name = nameof(Read) };
    public static OperationAuthorizationRequirement Update =
        new OperationAuthorizationRequirement { Name = nameof(Update) };
    public static OperationAuthorizationRequirement Delete =
        new OperationAuthorizationRequirement { Name = nameof(Delete) };
}

處理常式實作方式如下,使用 OperationAuthorizationRequirement 需求和資源 Document

public class DocumentAuthorizationCrudHandler :
    AuthorizationHandler<OperationAuthorizationRequirement, Document>
{
    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
                                                   OperationAuthorizationRequirement requirement,
                                                   Document resource)
    {
        if (context.User.Identity?.Name == resource.Author &&
            requirement.Name == Operations.Read.Name)
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }
}

上述處理常式會使用資源、使用者的身分識別和需求的 Name 屬性來驗證作業。

操作資源處理常式的挑戰與禁止

本節說明如何處理挑戰和禁止動作結果,以及挑戰和禁止有何差異。

如要呼叫作業性資源處理常式,請在頁面處理常式或動作中叫用 AuthorizeAsync 時指定作業。 下列範例會判斷是否允許已驗證的使用者檢視所提供的文件。

注意

下列程式碼範例假設驗證已執行並設定 User 屬性。

public async Task<IActionResult> OnGetAsync(Guid documentId)
{
    Document = _documentRepository.Find(documentId);

    if (Document == null)
    {
        return new NotFoundResult();
    }

    var authorizationResult = await _authorizationService
            .AuthorizeAsync(User, Document, Operations.Read);

    if (authorizationResult.Succeeded)
    {
        return Page();
    }
    else if (User.Identity.IsAuthenticated)
    {
        return new ForbidResult();
    }
    else
    {
        return new ChallengeResult();
    }
}

如果授權成功,則會傳回檢視文件的頁面。 如果授權失敗,但使用者有經過驗證,則傳回 ForbidResult 會通知任何驗證中介軟體授權失敗。 必須執行驗證時,會傳回 ChallengeResult。 針對互動式瀏覽器用戶端,將使用者重新導向至登入頁面可能較適合。