LoginSignup
7
8

More than 1 year has passed since last update.

ASP.NET Core MVC に Identity で認証機能を追加する

Posted at

やること

ASP.NET Core MVC の MVC アーキテクチャに Identity を組み込んで認証機能を追加します。
Visual Studio で提供される MVC + Identity のテンプレートでは MVC に Razor Pages を追加する形になっていて気持ち悪い...全部 MVC に組み込みたい!ということでこのようなことをやります。
開発環境は Visual Studio for Mac です。

作るもの

認証機能付きの簡単な ToDo アプリを作ってみます。
仕様は以下のようにします。

  • アカウントは名前とパスワードのみで登録
  • 各アカウントが登録するタスクは独立している
    • つまり、A さんが登録するタスクは B さんから見ることができない。逆も然り。
  • CRD だけ実装して、U は実装しない(別にしてもいいけど、省略)

やっていくぞ

1. 新規プロジェクトを作成

web アプリケーション (MVC), .NET Core3.1, 認証なし, と選択していきます。
スクリーンショット 2021-04-29 14.57.10.png
スクリーンショット 2021-04-29 14.57.22.png

2. NuGet パッケージ

必要になるものをあらかじめインストールしてしまいましょう。

以下の 6 つをインストールします。.NET Core 3.1 でやってるので、各パッケージのバージョンも 3.1.x で設定しておきます。

  • AspNetCore.Identity
  • AspNetCore.Identity.EntityFramework
  • AspNetCore.Identity.UI
  • EntityFrameworkCore
  • EntityFrameworkCore.Design
  • EntityFrameworkCore.Sqlite

スクリーンショット 2021-05-01 16.14.20.png

3. データベースコンテキストの作成

各ユーザーの認証情報はデータベースに格納されるので、まずはデータベースを使えるようにするところから。
普通にデータベース作るだけなら、DbContext を継承したクラスを作ればいいのですが、Identity で使うデータベースコンテキストは IdentityDbContext を継承します。このクラスには Users とか、認証情報を格納する DbSet がすでにプロパティに入っているので、継承さえしてしまえば OK です。

ToDoContext.cs
namespace ToDoIdentity.Data
{
    public class ToDoContext : IdentityDbContext
    {
        public ToDoContext(DbContextOptions<ToDoContext> options)
                : base(options) { }
    }
}

4. Identity を使うために Startup.cs を編集

ここがややこしい...ぶっちゃけおまじない状態

Startup.ConfigureServices

やることは 3 つ。

  • データベースコンテキストを使えるようにする
  • Identity を使えるようにする
  • 認証機能の設定をする

ConfigureServices に以下のようなコードを追加します。

Startup.cs
services.AddDbContext<ToDoContext>(options =>
    Configuration.GetConnectionString("DefaultConnection")));

// Identity を追加
services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = false)
    .AddEntityFrameworkStores<ToDoContext>();

// Identity を設定
services.Configure<IdentityOptions>(options =>
{
    // Password settings.
    options.Password.RequireDigit = false;
    options.Password.RequireLowercase = false;
    options.Password.RequireNonAlphanumeric = false;
    options.Password.RequireUppercase = false;
    options.Password.RequiredLength = 6;
    options.Password.RequiredUniqueChars = 1;

    // Lockout settings.
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    options.Lockout.MaxFailedAccessAttempts = 5;
    options.Lockout.AllowedForNewUsers = true;

    // User settings.
    options.User.AllowedUserNameCharacters = null; // UserName に使える文字の制限をなしにする
    options.User.RequireUniqueEmail = false;
});

services.ConfigureApplicationCookie(options =>
{
    // Cookie settings
    options.Cookie.HttpOnly = true;
    options.ExpireTimeSpan = TimeSpan.FromMinutes(5);

    options.LoginPath = "/Identity/Login";
    options.AccessDeniedPath = "/Identity/AccessDenied"; //AccessDenied は未実装
    options.SlidingExpiration = true;
});

services.AddDefaultIdentity<IdentityUser> で、ユーザー情報として使うクラスを設定します。ここではデフォルトの IdentityUser を使っていますが、IdentityUser を継承したクラスでも設定できます(詳しくはこのへんを見ると良い)
Identity の設定はこれを見れば良い。

Startup.Configure

app.UseAuthentication()app.UseRouting()app.UseAuthorization() の間に入れます。

Starup.cs
app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

これで、詳しいことはよくわからんが、HTTP リクエストが認証ミドルウェアを通るようになるらしい。

5. 認証機能の実装

さてここからバシバシとコードを書いていくわけですが、ここまでくれば普通の MVC の考え方を使っていけばいいです。
つまり、Identity の Model, View, Controller を作っていきます。

View

ログイン画面とアカウント登録画面を作って、それぞれにフォームを追加するだけ。

例えばログイン画面はこんな感じ

Login.cshtml
@model IdentityInputModel 
@{
    ViewData["Title"] = "Login";
}

<h1>ログイン</h1>

<div class="row">
    <div class="col-md-4">
        <form method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-group">
                <label asp-for="UserName" class="control-label"></label>
                <input asp-for="UserName" class="form-control" />
                <span asp-validation-for="UserName" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="Password" class="control-label"></label>
                <input type="password" asp-for="Password" class="form-control" />
                <span asp-validation-for="Password" class="text-danger"></span>
            </div>
            <div class="form-group">
                <input formmethod="post" type="submit" value="ログイン" class="btn btn-primary" />
            </div>
        </form>
    </div>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Model

View からフォームで post されたものを格納できれば良いです。

IdentityInputModel.cs
namespace ToDoIdentity.Models
{
    public class IdentityInputModel
    {
        public string UserName { get; set; }
        public string Password { get; set; }
    }
}

Controller

ここはちょっと普通の Controller と違う部分が出てきます。というのも、ここでログインしたりログアウトしたりという処理を行うからです。
まずはコンストラクタの引数で UserManagerSignInManager のインスタンスを受け取ります。これらはそれぞれ、リクエスト元のユーザー情報を取得するインスタンス、ログイン関係の処理を行うインスタンスです。

IdentityController.cs
public IdentityController(UserManager<IdentityUser> _userManager,
SignInManager<IdentityUser> _signInManager)
{
        userManager = _userManager;
        signInManager = _signInManager;
}

これらを使って、ログイン、ログアウト、アカウント登録を行っていきましょう。

ログイン
IdentityController.cs
var result = await signInManager.PasswordSignInAsync(model.UserName, model.Password, true, false);

これでログイン処理を行い、その結果が result に格納されます。ログインの成功・失敗によってリダイレクト先を変えたりできます。

ログアウト
IdentityController.cs
await signInManager.SignOutAsync();

これだけ!

アカウント登録
IdentityController
var user = new IdentityUser() { UserName = model.UserName };
var result = await userManager.CreateAsync(user, model.Password);

こっちは userManager を使って行います。ユーザーインスタンスとパスワードを引数に入れて登録です。これも成功したかどうかが result に入れられます。

6. 普通に ToDo アプリを作っていく

さて、これにて認証機能は実装できたので、あとは ToDo の CR(U)D を実装していくだけですね。
ところが、仕様をもう一度確認してみると

各アカウントが登録するタスクは独立している

とあります。
これは、ToDo モデルにユーザー ID を持たせることで実装できそうです。

ToDoItem.cs
namespace ToDoIdentity.Models
{
    public class ToDoItem
    {
        [Key]
        public int Id { get; set; }
        public string ToDo { get; set; }
        public string UserId { get; set; }
    }
}

本当は UserId を外部キーにした方がいいんですけど、ここでは省略。

さて、あとは Controller で ToDo モデルに UserId を渡すようにすれば OK です。Controller ではユーザーの情報を以下のようにして取得することができます。

ToDoItemController.cs
var newItem = new ToDoItem()
{
    UserId = userManager.GetUserId(User),
    ToDo = toDoName
};

このように userManager からユーザーの情報を取得できます。UserManager インスタンスは、IdentityController でやったのと同様に、コンストラクタの引数で受け取れます。

完成!!

スクリーンショット 2021-05-01 17.10.37.png
スクリーンショット 2021-05-01 17.10.51.png

できた〜!
ということでコードをこちらにあげています。参考にしてみてください!

おわり

ぶっちゃけ Identity は高機能すぎて使いこなせてないです...ちゃんと勉強しなきゃー

7
8
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
8