次の方法で共有


ASP.NET Coreでの要求ベースの承認

アプリへのサインイン時にアプリ ユーザーの ID が作成されると、ID プロバイダーはユーザーの ID に 1 つ以上の 要求 を割り当てることができます。 要求は、サブジェクト (ユーザー、アプリまたはサービス、またはデバイス/コンピューター) とは何かを表す名前の値のペアであり、サブジェクトが実行できる操作ではありません。 要求は、承認プロセス中にデータやその他のセキュリティで保護されたリソースへのアクセス権を決定するためにアプリによって評価できます。また、サブジェクトに関する認証の決定を行ったり、表現したりするために使用することもできます。 ID には、複数の値を持つ複数の要求を含めることができます。また、同じ型の複数の要求を含めることができます。 この記事では、ASP.NET Core アプリで承認の要求チェックを追加する方法について説明します。

この記事では、 Razor コンポーネントの例を使用し、 Blazor 承認シナリオに焦点を当てます。 その他の Blazor ガイダンスについては、「 その他のリソース 」セクションを参照してください。 Razor Pages と MVC のガイダンスについては、次のリソースを参照してください。

サンプル アプリ

この記事の Blazor Web App サンプルは、BlazorWebAppAuthorization サンプル アプリ (dotnet/AspNetCore.Docs.Samples GitHub リポジトリ) (ダウンロード方法) です。 このサンプル アプリでは、事前構成済みの要求を含むシード処理されたアカウントを使用して、この記事のほとんどの例を示します。 詳細については、サンプルの README ファイル (README.md) を参照してください。

注意事項

このサンプル アプリでは、メモリ内データベースを使用してユーザー情報を格納しますが、運用環境のシナリオには適していません。 サンプル アプリはデモンストレーションのみを目的としており、運用環境のアプリの開始点として使用しないでください。

クレームチェックを追加する

要求ベースの承認チェック

AuthorizeView コンポーネント (AuthorizeView ドキュメントのBlazor コンポーネント) では、ポリシー ベースの承認がサポートされています。この場合、ポリシーには 1 つ以上の要求が必要です。 または、1 つ以上のポリシー チェックを使用したクレーム ベースの承認は、[Authorize] コンポーネントRazorを使用して設定できます。 開発者は、要求要件を表すポリシーを構築して登録する必要があります。 このセクションでは、基本的な概念について説明します。 完全な対象範囲については、「ASP.NET Core Blazor 認証と承認を参照してください。

最も単純な種類のクレーム ポリシーでは、クレームが存在することは調べられますが、値はチェックされません。

ポリシーの登録は、アプリの Program ファイルの Authorization サービス構成の一部として行われます。

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

WebApplicationBuilder.ConfigureApplication (参照元) は、UseAuthorization が登録されたときに、IAuthorizationHandlerProvider の呼び出しを自動的に追加します。これは、.NET 8 のリリース以降の ASP.NET Coreの動作です。 そのため、.NET 8 以降のサーバー側UseAuthorization アプリに対して明示的に Blazor を呼び出すことは技術的には冗長ですが、呼び出しは有害ではありません。 フレームワークによって既に呼び出された後に開発者コードで呼び出すのは、単に操作なしだけです。

.NET参照ソースへのドキュメント リンクは、通常、リポジトリの既定のブランチを読み込みます。これは、.NETの次のリリースの現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法 (dotnet/AspNetCore.Docs #26205)を参照してください。

ポリシーの登録は、アプリの Program ファイルの Authorization サービス構成の一部として行われます。

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

Blazor Serverアプリでは、UseAuthorizationを呼び出す行の後にUseAuthenticationを呼び出します (存在する場合)。

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

ポリシーの登録は、 Startup.ConfigureServices (Startup.cs) の承認サービス構成の一部として行われます。

services.AddAuthorization(options =>
{
    options.AddPolicy("EmployeeOnly", policy =>
        policy.RequireClaim("EmployeeNumber"));
});

Blazor Server アプリでは、UseAuthorizationを呼び出す行の後にStartup.ConfigureUseAuthenticationを呼び出します (存在する場合)。

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

Blazor WebAssemblyアプリは、AddAuthorizationCore ファイル内のProgramを呼び出して、承認サービスを追加します。

builder.Services.AddAuthorizationCore();

Policy [Authorize] プロパティを使用してポリシーを適用し、ポリシー名を指定します。 次の例では、 EmployeeOnly ポリシーによって、現在の ID に対する 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 ポリシーによって、現在の ID に対する 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>

汎用のクレーム チェックを追加する

要求値が 1 つの値ではない場合、またはパターン マッチング、要求発行者の確認、複雑な要求値の解析など、より柔軟な要求評価ロジックが必要な場合は、RequireAssertionHasClaimを使用します。 たとえば、次のポリシーでは、ユーザーの 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 での Policy ベースの承認」を参照してください。

複数のポリシーを評価する

複数の 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) は、ID を作成した認証ハンドラーに関係なく、常に異なるロールとして扱われます。

個別に、要求の 種類 の比較 ( http://schemas.microsoft.com/ws/2008/06/identity/claims/role など、要求の種類によってロール要求を検索するために使用) は、 ClaimsIdentity の実装によっては大文字と小文字が区別されるか、大文字と小文字が区別されない場合があります。 ASP.NET Core 8.0 以降では、Microsoft.IdentityModel は (AddJwtBearerAddOpenIdConnectAddWsFederation で使用され、また AddMicrosoftIdentityWebApp/AddMicrosoftIdentityWebApi 含む)、大文字と小文字を区別するクレーム型の一致を使用するトークン検証中に生成されます。

.NET ランタイムによって提供される既定の ClaimsIdentity (ほとんどの場合、すべての cookie ベースのフローを含む) では、大文字と小文字が区別されない要求の種類の照合が使用されます。

実際には、ロール要求の種類は ID の作成時に 1 回設定され、一貫して照合されるため、この区別がロールの承認に重要になることはほとんどありません。 ロール名とクレームタイプにおいて一貫性のある大文字小文字を常に使用することで、微妙な問題を回避します。

その他のリソース