2016年10月14日金曜日

ASP.NET Identityを利用して独自ユーザーを作る(前編)

ASP.NET Identityを初めて使っています。

非常に便利なのですが、VisualStudioでMVCテンプレートを選択して作成したプロジェクトでは、IdentityUserを継承しているためにメールアドレスや電話番号等が含まれており、シンプルに名前とパスワードだけを使いたいような場合にはオーバースペックですよね。
そこで、生成したプロジェクトをベースに、必要最小限のユーザー情報で登録・ログインなどができるように変更してみたいと思います。

ApplicationUserをIUserを実装するように変更し、パスワードを追加します。
public class ApplicationUser : IUser
{
    public string Id { getset; }
 
    public string UserName { getset; }
 
    public string PasswordHash { getset; }
 
    public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager)
    {
        var userIdentity = await manager.CreateIdentityAsync(thisDefaultAuthenticationTypes.ApplicationCookie);
        return userIdentity;
    }
}

コンテキストをDbContext派生にします。
public class ApplicationDbContext : DbContext
{
    public ApplicationDbContext()
        : base("DefaultConnection")
    {
    }
 
    public static ApplicationDbContext Create()
    {
        return new ApplicationDbContext();
    }
 
    public DbSet<ApplicationUser> ApplicationUsers { getset; }
}

IUserStoreを実装するストアクラスを新しく作ります。
public class ApplicationUserStore : IUserStore<ApplicationUser>
{
    private ApplicationDbContext _context;
 
    public ApplicationUserStore(ApplicationDbContext context)
    {
        this._context = context;
    }
 
    // IUserStoreインターフェースの実装は省略
}

作成したストアをユーザーマネージャーのコンストラクタに渡します。
public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public ApplicationUserManager(IUserStore<ApplicationUser> store)
        : base(store)
    {
    }
 
    public static ApplicationUserManager Create(IdentityFactoryOptions<ApplicationUserManager> options, IOwinContext context) 
    {
        var manager = new ApplicationUserManager(new ApplicationUserStore(context.Get<ApplicationDbContext>()));
 
        // 省略

        return manager;
    }
}

これでビルドが通るようになりました。
デバッグを開始し、登録しようとするとエラーが発生します。

ストアは IUserPasswordStore<TUser> を実装していません。


{
    var user = new ApplicationUser { UserName = model.Email, };
    var result = await UserManager.CreateAsync(user, model.Password);
    if (result.Succeeded)
    {

ということで、ストアクラスにIUserPasswordStoreも実装します。
public class ApplicationUserStore : IUserStore<ApplicationUser>, IUserPasswordStore<ApplicationUser>
{
    private ApplicationDbContext _context;
 
    public ApplicationUserStore(ApplicationDbContext context)
    {
        this._context = context;
    }
 
    public void Dispose()
    {
    }
 
    public Task<ApplicationUser> FindByNameAsync(string userName)
    {
        return this._context.ApplicationUsers.SingleOrDefaultAsync(x => x.UserName == userName);
    }
 
    public Task SetPasswordHashAsync(ApplicationUser user, string passwordHash)
    {
        return Task.Run(() => user.PasswordHash = passwordHash);
    }
 
    // その他のインターフェースの実装は省略
}

再度、登録しようとするとまたエラーが発生しました。

ストアは IUserEmailStore<TUser> を実装していません。


{
    var user = new ApplicationUser { UserName = model.Email, };
    var result = await UserManager.CreateAsync(user, model.Password);
    if (result.Succeeded)
    {

IUserPasswordStoreはまだしも、さすがにIUserEmailStoreを実装するわけにはいきません。
CreateAsyncメソッドをオーバーライドすることにしました。
public class ApplicationUserManager : UserManager<ApplicationUser>
{
    public override async Task<IdentityResult> CreateAsync(ApplicationUser user, string password)
    {
        var store = (ApplicationUserStore)this.Store;
        var identityResult = await UpdatePassword(store, user, password);
        if (!identityResult.Succeeded)
        {
            return identityResult;
        }
 
        await store.CreateAsync(user);
        return IdentityResult.Success;
    }
}

ストアクラスの作成処理はこんな感じです。
public class ApplicationUserStore : IUserStore<ApplicationUser>, IUserPasswordStore<ApplicationUser>
{
    public Task CreateAsync(ApplicationUser user)
    {
        user.Id = Guid.NewGuid().ToString();
        this._context.ApplicationUsers.Add(user);
        return this._context.SaveChangesAsync();
    }
}

これで登録ができるようになりました。
次回はログインです。

またいつか、どこかで。

0 件のコメント:

コメントを投稿