共用方式為


ASP.NET Core 中的角色基礎授權

當使用者身份在驗證後建立時,使用者可能屬於一個或多個 角色,反映使用者對存取資料及執行操作的各種授權。 例如,Tracy 可能屬於「管理員」和「使用者」角色,能存取應用程式中的管理網頁,而 Scott 可能只屬於「使用者」角色,無法存取管理資料或操作。 這些角色的建立和管理方式取決於授權程式的備份存放區。 角色會透過 ClaimsPrincipal.IsInRole揭露給開發者。 AddRoles 在設定應用程式身份系統時,必須呼叫以新增角色服務。

雖然角色是宣告,但並非所有宣告都是角色。 根據身份發行者的不同,角色可能包含一組用戶,可為群組成員申請宣告,也可能包含針對特定身份的實際宣告。 不過,宣告旨在提供個別使用者的資訊。 使用角色來向使用者添加宣告,可能會模糊使用者與其個別宣告之間的界限。 這種混淆是為什麼單頁申請(SPA)範本並非以角色為核心設計。 此外,對於從本地舊有系統遷移過來的組織來說,角色數量多年來的激增可能導致角色主張過大,無法被 SPA 用的代幣所包含。 若要保護 SPA,請參閱使用 Identity 來保護 SPA 的 Web API 後端

本文使用 Razor 元件範例,並聚焦於 Blazor 授權情境。 如需更多 Blazor 指引,請參閱 「附加資源 」章節。 關於 Razor 頁面與MVC指引,請參閱以下資源:

Identity 配置隨著 .NET 6 的發行而改變。 本文範例展示了在應用程式Identity檔案中配置Program服務的方法。 對於在 .NET 6 版本之前(以及在 .NET 8 發行 Blazor Web App 之前)的.NET應用程式,服務配置在 Startup.cs 檔案中的 Startup.ConfigureServices。 設定語法 Identity 顯示在配套 Razor 中的 Pages 角色授權文章MVC 角色授權文章 中。 請參考前述資源,並將文章版本選擇器設定為你應用程式目標的 .NET 版本。

範例應用程式

本文的 Blazor Web App 範例是 BlazorWebAppAuthorization 範例應用程式(dotnet/AspNetCore.Docs.Samples GitHub 倉庫)how to download)。 範例應用程式使用已建立的種子帳號與預先設定的角色來示範本文大部分範例。 欲了解更多資訊,請參閱範例的 README 檔案(README.md)。

謹慎

這個範例應用程式使用記憶體內資料庫來儲存使用者資訊,這並不適合生產環境。 範例應用程式僅供示範用途,不應作為生產應用程式的起點。

將角色服務新增至 Identity

在應用程式的Program設定中,透過指定角色類型後,再使用AddRoles將基於角色的授權服務註冊到Identity檔案中。 下列範例中的角色類型為 IdentityRole

builder.Services.AddDefaultIdentity<IdentityUser>( ... )
    .AddRoles<IdentityRole>()
    ...

前述程式碼需要 Microsoft.AspNetCore.Identity.UI NuGet 套件 以及 usingMicrosoft.AspNetCore.Identity 指令。

若應用程式需要細緻地手動控制建置Identity,請在AddRoles上呼叫AddIdentityCore

builder.Services.AddIdentityCore<IdentityUser>()
    .AddRoles<IdentityRole>()
    ...

在應用程式Startup.ConfigureServices設定中以角色類型呼叫Startup.cs,將基於角色的授權服務註冊在 AddRoles (Identity) 中。 下列範例中的角色類型為 IdentityRole

services.AddDefaultIdentity<IdentityUser>()
    .AddRoles<IdentityRole>()
    ...

前述程式碼需要 Microsoft.AspNetCore.Identity.UI NuGet 套件 以及 usingMicrosoft.AspNetCore.Identity 指令。

若應用程式需要細緻地手動控制建置Identity,請在AddRoles上呼叫AddIdentityCore

services.AddIdentityCore<IdentityUser>()
    .AddRoles<IdentityRole>()
    ...

在 Blazor Web Apps(.NET 8 或更新版本)中,不需要在 UseAuthorization 檔案中呼叫 Program

在Blazor Server應用程式中,請在Program檔案中呼叫UseAuthorization,位置是在呼叫UseAuthentication的那一行之後(如果存在的話):

app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();

在Blazor Server應用程式(非Blazor Web Apps)中,在呼叫UseAuthentication的行後,於Program檔案中呼叫UseAuthorization(如果存在的話):

app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();

在 Blazor Server 應用程式(而非 Blazor Web Apps)中,請在呼叫 UseAuthentication 的行(如果有的話)之後呼叫 UseAuthorizationStartup.ConfigureStartup.cs)。

app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();

Blazor WebAssembly 應用程式呼叫 AddAuthorizationCore 檔案 Program 以新增授權服務:

builder.Services.AddAuthorizationCore();

基於角色的授權檢查

角色為基礎的授權驗證:

AuthorizeView該元件(AuthorizeView文件中的Blazor元件)支援基於角色的授權。 本節涵蓋基本概念。 完整內容請參見 ASP.NET Core Blazor 認證與授權

在元件中以角色為基礎授權 Razor 內容時,請使用參數 AuthorizeView.Roles

在以下範例中:

  • 使用者必須擁有AdminSuperUser角色的角色聲明,才能查看第一個AuthorizeView元件的內容。
  • 為了同時要求 AdminSuperUser 角色要求,第二個範例會將 AuthorizeView 組件巢狀化。

Pages/RoleChecksWithAuthorizeView.razor

@page "/role-checks-with-authorizeview"

<h3>Role Checks with AuthorizeView</h3>

<AuthorizeView Roles="Admin, SuperUser">
    <p>User: @context.User.Identity?.Name</p>
    <p>You have an 'Admin' or 'SuperUser' role claim.</p>
</AuthorizeView>

<AuthorizeView Roles="Admin">
    <p>User: @context.User.Identity?.Name</p>
    <p>You have the 'Admin' role claim.</p>
    <AuthorizeView Roles="SuperUser" Context="innerContext">
        <p>User: @innerContext.User.Identity?.Name</p>
        <p>You have both 'Admin' and 'SuperUser' role claims.</p>
    </AuthorizeView>
</AuthorizeView>

上述程式碼會為內部 Context 元件建立 AuthorizeView,以防止 AuthenticationState 內容衝突。 在外部 AuthenticationState 中,AuthorizeView 內容會使用存取內容的標準方法 (@context.User) 來存取。 內部 AuthorizeView 的內容會在具名的 innerContext 環境 (@innerContext.User) 中存取。

[Authorize] 屬性 支援整個 Razor 元件的角色授權。 請使用 AuthorizeAttribute.Roles 參數。 以下程式碼限制元件存取權僅限於角色成員 Admin 的使用者。

Pages/RequireAdminRoleWithAuthorizeAttribute.razor

@page "/require-admin-role-with-authorize-attribute"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin")]

<h1>Require 'Admin' role with [Authorize] attribute</h1>

<p>You can only see this if you're in the 'Admin' role.</p>

多個角色可以用逗號分隔的清單來指定。 以下範例中,存取限制僅限於屬於Admin角色或角色的成員。

Pages/RequireAdminOrSuperUserRoleWithAuthorizeAttribute.razor

@page "/require-admin-or-superuser-role-with-authorize-attribute"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin, SuperUser")]

<h1>Require 'Admin' or 'SuperUser' role with [Authorize] attribute</h1>

<p>
    You can only see this if you're in the 'Admin' role or the 'SuperUser' role.
</p>

當套用多個屬性時,使用者必須是 所有 指定角色的成員。 以下範例需要兩者皆AdminSuperUser 角色。

Pages/RequireAdminAndSuperUserRolesWithAuthorizeAttributes.razor

@page "/require-admin-and-superuser-roles-with-authorize-attributes"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Roles = "Admin")]
@attribute [Authorize(Roles = "SuperUser")]

<h1>Require 'Admin' and 'SuperUser' roles with [Authorize] attributes</h1>

<p>
    You can only see this if you're in both the 'Admin' role 
    and the 'SuperUser' role.
</p>

角色匹配通常是區分大小寫的,因為角色名稱是透過 .NET 字串比較進行儲存與比較的。 例如, Admin (大寫A)不被視為與(小寫admin)相同的角色a。 欲了解更多資訊,請參閱 ASP.NET Core 中的基於宣告的授權

以政策為基礎的授權檢查

角色需求可用政策語法表達,應用程式在啟動時註冊政策,作為授權服務設定的一部分。

在以下範例中:

  • RequireAdminRole政策規定使用者必須處於該Admin角色中。
  • RequireSuperUserRole政策規定使用者必須處於該SuperUser角色中。
builder.Services.AddAuthorizationBuilder()
    .AddPolicy("RequireAdminRole",
         policy => policy.RequireRole("Admin"))
    .AddPolicy("RequireSuperUserRole",
         policy => policy.RequireRole("SuperUser"));
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdminRole",
        policy => policy.RequireRole("Admin"));
    options.AddPolicy("RequireSuperUserRole",
        policy => policy.RequireRole("SuperUser"));
});
services.AddAuthorization(options =>
{
    options.AddPolicy("RequireAdminRole",
        policy => policy.RequireRole("Admin"));
    options.AddPolicy("RequireSuperUserRole",
        policy => policy.RequireRole("SuperUser"));
});

對於使用 AuthorizeView 元件進行基於政策的授權,使用帶有單一政策名稱的 AuthorizeView.Policy 參數。

Pages/PassRequireAdminRolePolicy.razor

@page "/pass-requireadminrole-policy-with-authorizeview"

<h1>Pass 'RequireAdminRole' policy with AuthorizeView</h1>

<AuthorizeView Policy="RequireAdminRole">
    <p>You satisfy the 'RequireAdminRole' policy.</p>
</AuthorizeView>

若要處理使用者應該滿足數個原則之一的情況,請建立原則,確認使用者符合其他原則。

若要處理用戶必須同時滿足數個原則的情況,請採取下列 其中一 種方法:

  • 建立 AuthorizeView 的原則,確認使用者符合數個其他原則。

  • 將政策嵌套在多個 AuthorizeView 元件中。

    Pages/PassRequireAdminRoleAndRequireSuperUserRolePoliciesWithAuthorizeViews.razor

    @page "/pass-requireadminrole-and-requiresuperuserrole-policies-with-authorizeviews"
    
    <h1>
        Pass 'RequireAdminRole' and 'RequireSuperUserRole' policies with AuthorizeViews
    </h1>
    
    <AuthorizeView Policy="RequireAdminRole">
        <AuthorizeView Policy="RequireSuperUserRole" Context="innerContext">
            <p>
                You satisfy the 'RequireAdminRole' and 
                'RequireSuperUserRole' policies.
            </p>
        </AuthorizeView>
    </AuthorizeView>
    

如果同時設定 RolesPolicy ,則只有在這兩個條件都滿足時,授權才會成功。 也就是說,用戶必須至少屬於其中一個指定的角色 ,並 符合原則所定義的需求。

如果未指定 RolesPolicyAuthorizeView 便會使用預設原則:

  • 已驗證 (已登入) 的使用者視為已授權。
  • 未驗證 (已登出) 的使用者視為未授權。

與通常區分大小寫的角色匹配不同,ASP.NET Core 策略名稱查找通常是不區分大小寫,因此 RequireAdminRolerequireadminrole 指的是同一個策略。

政策是透過屬性Razor上的屬性Policy套用到整個[Authorize]元件。

Pages/PassRequireAdminRolePolicyWithAuthorizeAttribute.razor

@page "/pass-requireadminrole-policy-with-authorize-attribute"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "RequireAdminRole")]

<h1>Pass RequireAdminRole policy with [Authorize] attribute</h1>

<p>You can only see this if the 'RequireAdminRole' policy is satisfied.</p>

若要在需求中指定多個允許的角色,請將這些角色指定為方法的 RequireRole 參數。 以下範例中,若使用者屬於 AdminORSuperUser 角色,則被授權:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("ElevatedRights", policy =>
        policy.RequireRole("Admin", "SuperUser"));
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy =>
        policy.RequireRole("Admin", "SuperUser"));
});
services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy =>
        policy.RequireRole("Admin", "SuperUser"));
});

如果你想讓政策要求所有前述角色,要麼將角色串接到政策建置器,要麼在 Lambda 語句中分別指定給政策建置器。

依賴於政策編輯器:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("ElevatedRights", policy => 
        policy
            .RequireRole("Admin")
            .RequireRole("SuperUser"));

或者,也可以使用 lambda 表達式:

builder.Services.AddAuthorizationBuilder()
    .AddPolicy("ElevatedRights",
        policy =>
        {
            policy.RequireRole("Admin");
            policy.RequireRole("SuperUser");
        });
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy => 
        policy
            .RequireRole("Admin")
            .RequireRole("SuperUser"));
});

或者,也可以使用 lambda 表達式:

builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights",
        policy =>
        {
            policy.RequireRole("Admin");
            policy.RequireRole("SuperUser");
        });
});
services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights", policy => 
        policy
            .RequireRole("Admin")
            .RequireRole("SuperUser"));
});

或者,也可以使用 lambda 表達式:

services.AddAuthorization(options =>
{
    options.AddPolicy("ElevatedRights",
        policy =>
        {
            policy.RequireRole("Admin");
            policy.RequireRole("SuperUser");
        });
});

Windows 認證安全群組作為應用程式角色

當應用程式完成設定為Windows認證Blazor特定指引),且客戶端與伺服器機屬於同一Windows域後,使用者的安全群組會自動包含在使用者的 ClaimsPrincipal 中作為申訴。

當應用程式被設定為Windows認證且客戶端與伺服器機屬於同一Windows域後,使用者的安全群組會自動作為申訴納入使用者的ClaimsPrincipal

使用 Windows 認證時,User.Identity 通常是 WindowsIdentity,你可以用以下程式碼檢索 SID 群組聲明或確認使用者是否處於某角色,其中 {DOMAIN} 佔位符為網域,{SID GROUP NAME} 為 SID 群組名稱:

if (User.Identity is WindowsIdentity windowsIdentity)
{
    var groups = windowsIdentity.Groups;

    // If needed, obtain a list of the SID groups
    var securityGroups = 
        groups.Select(g => g.Translate(typeof(NTAccount)).ToString()).ToList();

    // If needed, obtain the user's Windows identity name
    var windowsIdentityName = windowsIdentity.Name;

    // Check if the user is in a specific SID group
    if (User.IsInRole(@"{DOMAIN}\{SID GROUP NAME}"))
    {
        // User is in the specified group
    }
    else
    {
        // User isn't in the specified group
    }
}
else
{
    // The user isn't authenticated with Windows Authentication
}

若想了解如何在 Blazor 應用程式中將 SID 群組聲明轉換為人類可讀值的相關程式碼示範,請參閱 Blazor Web App 中的 元件。 這種檢索 SID 群組聲明的方法,可以與新增IClaimsTransformation聲明結合,在使用者經過驗證時建立自訂角色聲明。

類似前述範例的 SID 群組聲明擷取方法,可以與添加的聲明一同使用,以便在使用者驗證時建立自訂角色聲明。

其他資源