ASP.NET Core MVC にて、Entity Frameworkを使わずにASP.NET Identityを利用する Part.1

前回までのあらすじ

準備をした!

mrgchr.hatenablog.com

UserManagerとSingnInManagerについて調べる

ASP.NET Ideneity(以下、Identity)では、ユーザー管理についてはUserManager、認証についてはSignInManager、権限管理についてはRoleManagerと3つのManagerクラスを利用してユーザー認証や管理を行います。

サンプルなどでは、それらのオブジェクトはDIコンテナによって管理されることが多いようです。

ConfigureServices(Startup.cs)

UserMangerなどはStartup.cs内のConfigureServicesにて、

services.AddIdentity<TUser, TRole>();

を追加することにより、DIにより管理されるようになります。

しかしながら、TUser, TRoleをまだ定義していないのでまずはそちらから片付けます。

ちなみに今回はRoleは利用しませんが用意する必要があります。

TUser, TRoleのクラスを実装する

では、さっそくTUserに相当するクラスを作りましょう。

ちなみにEneityFramework版(AspNetCore.Identity.EntityFrameworkCore)にはIdentityUser(IdentityUser)というクラスが用意されています。

以前のIdentityにはIUser、IUserインタフェースが用意されていましたが、ASP.NET Coreではそれらは廃止されているようです。

//Models/ApplicationUser.cs

public class ApplicationUser
{
  public ApplicationUser()
  {
    Id = Guid.NewGuid();
  }

  public ApplicationUser(string loginName) : this()
  {
    LoginName = loginName;
  }

  public Guid Id { get; set; }

  public string LoginName { get; set; }

  public string NormalizedLoginName { get; set; }

  public string ScreenName { get; set; }

  public string Email { get; set; }

  public string PasswordHash { get; set; }

  public string UserId => Id.ToString();
}

LoginNameについて補足すると、EF版のIdentityUser内UserNameに相当するものです。

UserManagerやこれから実装するIUserStoreには、"Name"という単語がよく出てきます。
これは「重複可能なユーザーの名前(≒スクリーンネーム)」ではなくて、「ユーザーを区別する名前(≒ログインID)」のようなものらしいため、区別のためにこのような名前にしておきました。
(デフォルトでは、この"Name"として使える文字(AllowedUserNameCharacters)は[A-Za-z0-9-._@+]となっており(UserOptions.cs)、メールアドレスを意識したものであると思われます)

また、UserIdですが、文字列型のIdとして比較する必要があるのでとりあえず用意しました。
なくてもどうにでもなると思います。ださいし。

TRole相当のApplicationRoleは適当です。使わないし。

//Models/ApplicationRole.cs

public class ApplicationRole
{
  public Guid Id { get; set; }

  public string Name { get; set; }
}

とりあえず、実行して失敗してみる

Startup.csに先ほどのものを追加してみます。

//Starup.cs ConfigureServices

services
.AddIdentity<ApplicationUser, ApplicationRole>()
.AddDefaultTokenProviders();

ついでにConfigureにUseIdentityを追加します。

//Starup.cs Configure

app.UseIdentity();

これによりクッキー認証が利用できます。

具体的には下記のようなコードが実行されます。

//AspNetCore.Identity BuilderExtention.cs

var options = app.ApplicationServices.GetRequiredService<IOptions<IdentityOptions>>().Value;
app.UseCookieAuthentication(options.Cookies.ExternalCookie);
app.UseCookieAuthentication(options.Cookies.TwoFactorRememberMeCookie);
app.UseCookieAuthentication(options.Cookies.TwoFactorUserIdCookie);
app.UseCookieAuthentication(options.Cookies.ApplicationCookie);

さあ!失敗してみよう!

とりあえず動かしてみましょう!ユーザークラスしか作っていないため当然失敗します。

HomeControllerに下記を追加します。

public async Task<IActionResult> Register([FromServices]UserManager<ApplicationUser> userManager)
{
  var user = new ApplicationUser { LoginName = "foo", Email = "foo@bar.com", ScreenName="Foo Bar" };
  var password = "Pa$$w0rd";
  var result = await userManager.CreateAsync(user, password);

  return View("Index");
}

そしてデバッグ実行し、/Home/Registerにアクセスすると失敗します。

f:id:mrgchr:20161127135649p:plain

UserManagerにInjectするIUserStore実装が見つからないよ!と言われました。

このIUserStoreの実装を用意する必要があります。
IUserStoreの実装がデータベースへのアクセスなどを持つことになります。

今日はここまで

長くなったので今日はここまでです。

mrgchr.hatenablog.com

おまけ

AddIdentity()により追加されるサービスの一覧です。

ServiceType Implementation(Type or Instance)
Extensions.Options.IConfigureOptions<AspNetCore.Authentication.SharedAuthenticationOptions> Extensions.Options.ConfigureOptions<AspNetCore.Authentication.SharedAuthenticationOptions>
AspNetCore.DataProtection.Internal.IActivator AspNetCore.DataProtection.RC1ForwardingActivator
AspNetCore.Identity.IdentityMarkerService AspNetCore.Identity.IdentityMarkerService
AspNetCore.Identity.IUserValidator AspNetCore.Identity.UserValidator
AspNetCore.Identity.IPasswordValidator AspNetCore.Identity.PasswordValidator
AspNetCore.Identity.IPasswordHasher AspNetCore.Identity.PasswordHasher
AspNetCore.Identity.ILookupNormalizer AspNetCore.Identity.UpperInvariantLookupNormalizer
AspNetCore.Identity.IRoleValidator AspNetCore.Identity.RoleValidator
AspNetCore.Identity.IdentityErrorDescriber AspNetCore.Identity.IdentityErrorDescriber
AspNetCore.Identity.ISecurityStampValidator AspNetCore.Identity.SecurityStampValidator
AspNetCore.Identity.IUserClaimsPrincipalFactory AspNetCore.Identity.UserClaimsPrincipalFactory<TUser, TRole>
AspNetCore.Identity.UserManager AspNetCore.Identity.UserManager
AspNetCore.Identity.SignInManager AspNetCore.Identity.SignInManager
AspNetCore.Identity.RoleManager AspNetCore.Identity.RoleManager
Extensions.Options.IConfigureOptions<AspNetCore.Builder.IdentityOptions> Extensions.Options.ConfigureOptions<AspNetCore.Builder.IdentityOptions>

また、AddDefaultTokenProviders()により下記が追加されます。

ServiceType Implementation(Type or Instance)
Extensions.Options.IConfigureOptions<AspNetCore.Builder.IdentityOptions> Extensions.Options.ConfigureOptions<AspNetCore.Builder.IdentityOptions>
AspNetCore.Identity.DataProtectorTokenProvider AspNetCore.Identity.DataProtectorTokenProvider
Extensions.Options.IConfigureOptions<AspNetCore.Builder.IdentityOptions> Extensions.Options.ConfigureOptions<AspNetCore.Builder.IdentityOptions>
AspNetCore.Identity.EmailTokenProvider AspNetCore.Identity.EmailTokenProvider
Extensions.Options.IConfigureOptions<AspNetCore.Builder.IdentityOptions> Extensions.Options.ConfigureOptions<AspNetCore.Builder.IdentityOptions>
AspNetCore.Identity.PhoneNumberTokenProvider AspNetCore.Identity.PhoneNumberTokenProvider