當應用程式使用者登入應用程式時建立身份時,身份提供者可能會為使用者的身份指派一項或多項 聲明 。 理賠是一組名稱值對,代表主體(使用者、應用程式或服務,或裝置/電腦)是什麼,而非主體能做什麼。 申請申請可在授權過程中評估,以確定對資料及其他安全資源的存取權,並可用於對主體做出或表達身份驗證決策。 同一身份可以包含多個具有多個值的權利要求,也可以包含多個相同類型的權利要求。 本文說明如何在 ASP.NET Core 應用程式中新增授權的理賠檢查。
本文使用 Razor 元件範例,並聚焦於 Blazor 授權情境。 如需更多 Blazor 指引,請參閱 「附加資源 」章節。 關於 Razor 頁面與MVC指引,請參閱以下資源:
範例應用程式
本文的 Blazor Web App 範例是 BlazorWebAppAuthorization 範例應用程式(dotnet/AspNetCore.Docs.Samples GitHub 倉庫) (how to download)。 範例應用程式使用預設帳號並預先設定的宣告,用於展示本文中的大部分示例。 欲了解更多資訊,請參閱範例的 README 檔案(README.md)。
謹慎
這個範例應用程式使用記憶體內資料庫來儲存使用者資訊,這並不適合生產環境。 範例應用程式僅供示範用途,不應作為生產應用程式的起點。
新增理賠檢查
宣告型授權檢查:
- 是宣告式,並透過政策指定,目前使用者必須提出這些聲明才能存取所請求的資源。
- 應用於 Razor 元件(本文範例)、 Razor 頁面,或 MVC 控制器,或控制器內的動作。
AuthorizeView該組件(AuthorizeView文件中的Blazor組件)支援基於政策的授權,當保單需要一個或多個理賠時。 或者,也可以透過一個或多個策略檢查,利用[Authorize]元件中的Razor屬性來設定基於聲明的授權。 開發商必須建立並登記一份表達理賠要求的保單。 本節涵蓋基本概念。 完整內容請參見 ASP.NET Core Blazor 認證與授權。
最簡單的宣告策略類型會檢查宣告是否存在,而不會檢查其值。
政策註冊會在應用程式 Program 檔案中的授權服務設定中進行:
builder.Services.AddAuthorizationBuilder()
.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
備註
WebApplicationBuilder.ConfigureApplication(reference source)在註冊 UseAuthorization 時自動新增 IAuthorizationHandlerProvider 呼叫,這也是自 .NET 8 發布以來 ASP.NET Core 的行為。 因此,在 .NET 8 或更高版本中,明確呼叫 UseAuthorization 用於伺服器端 Blazor 應用程式,在技術上是多餘的,但這樣的呼叫並無害處。 在框架已經呼叫過之後,在開發人員程式碼中再次呼叫它,只是無操作 (no-ops),即不會產生任何效果。
備註
文件連結指向.NET參考來源通常會載入倉庫的預設分支,代表下一版 .NET 的當前開發進度。 若要選取特定發行版本的標籤,請使用「切換分支或標籤」下拉式清單。 欲了解更多資訊,請參閱 如何選擇 ASP.NET Core原始碼版本標籤(dotnet/AspNetCore.Docs #26205)。
政策註冊會在應用程式 Program 檔案中的授權服務設定中進行:
builder.Services.AddAuthorizationBuilder()
.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber"));
在Blazor Server應用程式中,請在呼叫UseAuthentication之後呼叫UseAuthorization(如果有的話):
app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();
註冊政策作為授權服務配置的一部分,在
services.AddAuthorization(options =>
{
options.AddPolicy("EmployeeOnly", policy =>
policy.RequireClaim("EmployeeNumber"));
});
在Blazor Server應用程式中,如果存在UseAuthentication,請在呼叫UseAuthentication的語句之後呼叫UseAuthorization於Startup.Configure:
app.UseAuthentication(); // Only present if not called internally
app.UseAuthorization();
Blazor WebAssembly 應用程式呼叫 AddAuthorizationCore 檔案 Program 以新增授權服務:
builder.Services.AddAuthorizationCore();
利用Policy屬性上的[Authorize]屬性來指定政策名稱,套用該政策。 以下範例中, EmployeeOnly 政策檢查是否存在 EmployeeNumber 對當前身份的主張:
對於使用 AuthorizeView 元件進行基於政策的授權,使用帶有單一政策名稱的 AuthorizeView.Policy 參數。
Pages/PassEmployeeOnlyPolicyWithAuthorizeView.razor:
@page "/pass-employeeonly-policy-with-authorizeview"
<h1>Pass 'EmployeeOnly' policy with AuthorizeView</h1>
<AuthorizeView Policy="EmployeeOnly">
<Authorized>
<p>You satisfy the 'EmployeeOnly' policy.</p>
</Authorized>
<NotAuthorized>
<p>You <b>don't</b> satisfy the 'EmployeeOnly' policy.</p>
</NotAuthorized>
</AuthorizeView>
或者,利用Policy屬性上的[Authorize]屬性指定政策名稱以套用政策。 以下範例中, EmployeeOnly 政策檢查是否存在 EmployeeNumber 對當前身份的主張:
Pages/PassEmployeeOnlyPolicyWithAuthorizeAttribute.razor:
@page "/pass-employeeonly-policy-with-authorize-attribute"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "EmployeeOnly")]
<h1>Pass 'EmployeeOnly' policy with [Authorize] attribute</h1>
<p>You satisfy the 'EmployeeOnly' policy.</p>
你可以在建立政策時指定允許的值清單。 以下政策僅適用於員工編號為1、2、3、4或5的員工:
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();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddAuthorization(options =>
{
options.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();
services.AddAuthorization(options =>
{
options.AddPolicy("Founder", policy =>
policy.RequireClaim("EmployeeNumber", "1", "2", "3", "4", "5"));
});
Pages/PassFounderPolicyWithAuthorizeView.razor:
@page "/pass-founder-policy-with-authorizeview"
<h1>Pass 'Founder' policy with AuthorizeView</h1>
<AuthorizeView Policy="Founder">
<Authorized>
<p>You satisfy the 'Founder' policy.</p>
</Authorized>
<NotAuthorized>
<p>You <b>don't</b> satisfy the 'Founder' policy.</p>
</NotAuthorized>
</AuthorizeView>
新增泛型宣告檢查
如果理賠金額不是單一值,或你需要更靈活的理賠評估邏輯,例如模式匹配、檢查理賠發件人,或解析複雜的理賠金額,請使用 RequireAssertion 。HasClaim 例如,以下政策要求使用者 email 的主張必須以特定網域結尾:
builder.Services.AddAuthorizationBuilder()
.AddPolicy("ContosoOnly", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
c.Type == "email" &&
c.Value.EndsWith("@contoso.com", StringComparison.OrdinalIgnoreCase))));
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("ContosoOnly", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
c.Type == "email" &&
c.Value.EndsWith("@contoso.com", StringComparison.OrdinalIgnoreCase))));
});
services.AddAuthorization(options =>
{
options.AddPolicy("ContosoOnly", policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(c =>
c.Type == "email" &&
c.Value.EndsWith("@contoso.com", StringComparison.OrdinalIgnoreCase))));
});
欲了解更多資訊,請參閱 ASP.NET Core 中的基於策略的授權。
評估多項政策
多重政策透過多個 AuthorizeView 元件套用。 內部元件要求使用者通過其政策以及父元件的所有政策 AuthorizeView 。
下列範例:
- 需要一項
CustomerServiceMember規範,這表明使用者屬於組織的客服部門,因為他們擁有一項Department價值為Customer Service的索賠。 - 同時也需要一個
HumanResourcesMember政策,表示使用者屬於組織的人力資源部門,因為他們有一個Department值為Human Resources的主張。
在應用程式的 Program 檔案中:
builder.Services.AddAuthorizationBuilder()
.AddPolicy("CustomerServiceMember", policy =>
policy.RequireClaim("Department", "Customer Service"))
.AddPolicy("HumanResourcesMember", policy =>
policy.RequireClaim("Department", "Human Resources"));
在應用程式的 Program 檔案中:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("CustomerServiceMember", policy =>
policy.RequireClaim("Department", "Customer Service"));
options.AddPolicy("HumanResourcesMember", policy =>
policy.RequireClaim("Department", "Human Resources"));
});
在 Startup.ConfigureServices (Startup.cs) 中:
services.AddAuthorization(options =>
{
options.AddPolicy("CustomerServiceMember", policy =>
policy.RequireClaim("Department", "Customer Service"));
options.AddPolicy("HumanResourcesMember", policy =>
policy.RequireClaim("Department", "Human Resources"));
});
以下範例使用元件 AuthorizeView 。
Pages/PassCustomerServiceMemberAndHumanResourcesMemberPoliciesWithAuthorizeViews.razor:
@page "/pass-customerservicemember-and-humanresourcesmember-policies-with-authorizeviews"
<h1>Pass 'CustomerServiceMember' and 'HumanResourcesMember' policies with AuthorizeViews</h1>
<AuthorizeView Policy="CustomerServiceMember">
<Authorized>
<p>User: @context.User.Identity?.Name</p>
<AuthorizeView Policy="HumanResourcesMember" Context="innerContext">
<Authorized>
<p>
You satisfy the 'CustomerServiceMember' and 'HumanResourcesMember' policies.
</p>
</Authorized>
<NotAuthorized>
<p>
You satisfy the 'CustomerServiceMember' policy, but you <b>don't</b> satisfy
the 'HumanResourcesMember' policy.
</p>
</NotAuthorized>
</AuthorizeView>
</Authorized>
<NotAuthorized>
<p>
You <b>don't</b> satisfy the 'CustomerServiceMember' policy.
</p>
</NotAuthorized>
</AuthorizeView>
以下範例使用屬性[Authorize]。
Pages/PassCustomerServiceMemberAndHumanResourcesMemberPoliciesWithAuthorizeAttributes.razor:
@page "/pass-customerservicemember-and-humanresourcesmember-policies-with-authorize-attributes"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize(Policy = "CustomerServiceMember")]
@attribute [Authorize(Policy = "HumanResourcesMember")]
<h1>
Pass 'CustomerServiceMember' and 'HumanResourcesMember' policies with [Authorize] attributes
</h1>
<p>
You satisfy the 'CustomerServiceMember' and 'HumanResourcesMember' policies.
</p>
對於較複雜的保單,例如申請出生日期,計算年齡,再確認年齡是否為21歲或以上,則可使用RequireAssertion或撰寫自訂保單處理工具。 當你需要存取依賴注入服務或想要一個可重用、可測試的授權元件時,自訂政策處理器非常有用。
理賠案件敏感性
理賠金額會使用 StringComparison.Ordinal進行比較。 這表示 Admin (大寫 A)與 admin (小寫 a)總是被視為不同角色,無論身份由哪位認證處理者建立。
另外,理賠 類型 比較(用於依理賠類型定位角色理賠,例如 http://schemas.microsoft.com/ws/2008/06/identity/claims/role),可能根據實作方式而異,或不區分 ClaimsIdentity 大小寫。 在 ASP.NET Core 8.0 或更新版本中(AddJwtBearer、AddOpenIdConnect、AddWsFederation 及 AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApi 中使用),在代幣驗證時會產生 CaseSensitiveClaimsIdentity,該驗證使用大小寫敏感的宣告類型匹配。
.NET執行時提供的預設ClaimsIdentity(用於大多數情況,包括所有基於cookie的流程)仍然使用不區分大小寫的宣告類型匹配。
實務上,這種區分對角色授權來說很少有影響,因為角色主張類型在身份建立時只設定一次,且會一致匹配。 角色名稱和權限類型務必使用一致的大小寫,以避免細微問題。