前回Identityを組み込んでDBをPostgreSQLにしたプロジェクトに、オートマイグレーションを設定し初期のロールと管理ユーザーを登録します。
全体
・プロジェクトの作成からデータベースの設定まで(前々回)
・オートマイグレーションとロールと管理ユーザーの登録(前回)
・不要な認証用ページの削除(本稿)
-不要な認証用ページの削除-
環境
- VisualStudio2019 Ver.16.2.3
- ASP.NET Core 2.2
- PostgreSQL 9.6 インストール済み、接続用のアカウント作成済み
- EntityFramework
Identityをスキャフォールディングしてみる
ロールの効果を試すために、デフォルトのIdentityの機能を変更するために、スキャフォールディングしてソースを変更します。
スキャフォールディングを呼び出し(プロジェクトを右クリックして表示されるメニューで[追加] -> [新規スキャフォールディングアイテム]を選択)表示されたダイアログの左のメニューで「ID」を選び、右に表示された「ID」を選んで「追加」ボタンをクリックします。(この辺りはどんどん変わりそう)
次に表示されるダイアログで変更したいものを選びますが、とりあえず全てを選択して下さい。データコンテキストはコンボから表示されるものをえらんで、「追加」ボタンをクリックします。
実施すると「Areas/Identity」にソースができます。
不要なページの確認
Identity関連のユーザーインタフェースをスキャフォールディングで基本のソースを生成しましたが、見ていただくとわかるように多くの画面が展開されます。これらの画面は全てデフォルトで設定されており、スキャフォールディングしたページが優先されますが、このソースを消しても該当のページはなくならず、実行すると同じように動作してしまいます。画面上でリンクを消せば普通の方は表示できませんが、URLを直接入力すると表示されてしまいます。
コンシューマー向けのページを作るならそのまま使うこともできるのでしょうが、企業内部向けなどで利用者が自身を登録したり、登録内容を変更することができるとよくない場合はそれらの機能を停止する必要があります。
不要なページを削除する方法(1) - 【力技】不要なページの中身を空にする -
先のIdentity関連のスキャフォールディングで生成したページの中身を空にします。具体的には例えば「Identity/Account/Register.cshtml」の中身をクリアして以下のようにしてしまいます。
@page
@model RegisterModel
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace WebApplication1.Areas.Identity.Pages.Account
{
[AllowAnonymous]
public class RegisterModel : PageModel
{
public IActionResult OnGetAsync()
{
return NotFound();
}
}
}
これで、このページが呼ばれてもNotFoundで返るようになります。
不要なページを削除する方法(2) - 「AddDefaultIdentity」を使わない -
先の方法で一応不要なページが内容にはできるのですが、わざわざデフォルトのページを作成してNotFoundを返すのは、ソースの見た目的にもうれしくありません。
そこでもう少し調べてみて、結局「AddDefaultIdentity()」を使わない、というよりはその処理中に呼ばれている「AddDefaultUI()」を使わない方法がいいかなということで以下のようにしてみました。
まず、「AddDefaultIdentity」をやめます。マイクロソフトのページに下記のような中身がGitHubに記載されていますので、それを基に変更します。
public static IdentityBuilder AddDefaultIdentity<TUser>(this IServiceCollection services, Action<IdentityOptions> configureOptions) where TUser : class
{
services.AddAuthentication(o =>
{
o.DefaultScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies(o => { });
return services.AddIdentityCore<TUser>(o =>
{
o.Stores.MaxLengthForKeys = 128;
configureOptions?.Invoke(o);
})
.AddDefaultUI()
.AddDefaultTokenProviders();
}
これを「Startup.cs」に適用して「ConfigureServices(IServiceCollection services)」を以下のようにします。
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseNpgsql(
Configuration.GetConnectionString("DefaultConnection")));
// この部分を下の内容で置き換える
//services.AddDefaultIdentity<IdentityUser>()
// //.AddDefaultUI(UIFramework.Bootstrap4)
// .AddRoles<IdentityRole>() // ロールを利用するためにのサービスを追加
// .AddEntityFrameworkStores<ApplicationDbContext>();
// ここからが「AddDefaultIdentity」を置き換えたもの
services.AddAuthentication(o =>
{
o.DefaultScheme = IdentityConstants.ApplicationScheme;
o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
})
.AddIdentityCookies(o => { });
var builder = services.AddIdentityCore<IdentityUser>(o => // 返り値のビルダーを利用するために取得
{
o.Stores.MaxLengthForKeys = 128;
o.User.RequireUniqueEmail = false;
})
//.AddDefaultUI() // これは削除する
.AddDefaultTokenProviders()
.AddRoles<IdentityRole>() // ロールを利用するためにのサービスを追加
.AddEntityFrameworkStores<ApplicationDbContext>();
// AddDefaultUIのページ作成部分を削除したメソッドを呼びたし(後ろに記載)
AddDefaultUIExceptPages(builder);
// ページ作成時に設定されているらしい値をここで設定しておく(LoginPathなどがデフォルトから変更されているらしい)
services.ConfigureApplicationCookie(options =>
{
options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.Cookie.Name = "YourAppCookieName";
options.Cookie.HttpOnly = true;
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
// ReturnUrlParameter requires
//using Microsoft.AspNetCore.Authentication.Cookies;
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.SlidingExpiration = true;
});
// ここまでが書き換えた内容
services.AddMvc(config =>
{
// ASP.NET Core MVC で認証の機能を追加する
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
/// <summary>
/// AddDefaultUIでページ作成を外した処理
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
void AddDefaultUIExceptPages(IdentityBuilder builder)
{
builder.AddSignInManager(); // こいつがないとサインインができない
// AddRelatedParts(builder, framework); <<= おそらくここですべてのページを作っているのでコメントアウト
//builder.Services.ConfigureOptions( <<= なにやらいろいろしているようですが、「IdentityDefaultUIConfigureOptions」がinnerなのでコメントアウト、将来使うかも
// typeof(IdentityDefaultUIConfigureOptions<>)
// .MakeGenericType(builder.UserType));
builder.Services.TryAddTransient<IEmailSender, EmailSender>(); // この部分はメールを利用しないなら消してもよさそう、消す場合はclass「EmailSender」も不要
builder.Services.Configure<DefaultUIOptions>(o => o.UIFramework = UIFramework.Bootstrap3); // これは将来的にIdentity画面を変更してBootstrapを利用しないなら不要かと思う
}
置き換えの際に「AddDefaultUI()」を削除し、代わりに作成した「AddDefaultUIExceptPages(IdentityBuilder builder)」を利用します。これは「AddDefaultUI()」の処理を書き換えたもので一部不明な処理をコメントアウトしていますが、以降の処理で必要となれば復帰させる必要があるかもしれません。ちなみに「builder.Services.TryAddTransient();」の部分は2段階認証やパスワードリセットでEmailを利用しない場合は「internal class EmailSender : IEmailSender」のクラスと共に不要です。
/// <summary>
/// DefaultUIで定義さえているがInner classなのでここで別に定義している
/// </summary>
internal class EmailSender : IEmailSender
{
public Task SendEmailAsync(string email, string subject, string htmlMessage)
{
return Task.CompletedTask;
}
}
これでIdentityで自動的に作成されるページ類は作成されないようです。不要なソースを作る必要が無いのですっきりしました。
実際にスキャフォールディングで作成したページ関連をすべて削除し、「Login」のみスキャフォールディングで作成すると、「Regist」などの他のページは存在しなくなります。
基本的にはこれでIdentityを必要な部分だけ使って業務処理用のWebアプリを作れると思うので、一旦終了です。
あとで、ポリシーベースの承認や、ページモデルのアトリビュートへの記述以外の承認方法を記載するかもしれません。
パスワードの設定変更などは、いろいろと記載されている方がおられるのでそちらをご参考に。