しばやん雑記

ASP.NET とメイドさんが大好きなフリーランスのプログラマーのブログ

ASP.NET Core MVC 1.0 で Cookie を使ったユーザー認証を独自に実装する

ASP.NET MVC 5 までは FormsAuthentication を使って実装していた部分を、ASP.NET Core MVC 1.0 でも同じように独自で実装したいときにどうすればいいのか調べました。

公式のサンプルは Identity を使っていますが、ドキュメントに Identity を使わない方法もありました。単純に認証クッキーだけ使いたい場合を想定しています。

Using Cookie Middleware without ASP.NET Core Identity — ASP.NET documentation

要するに FormsAuthentication と同じことをしたいだけです。ASP.NET Core はコントロール可能な部分が増えていて、個人的にはかなりよい感じです。

Cookie 認証を追加

まずは CookieAuthentication ミドルウェアの設定を追加します。インストールは忘れずに。

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationScheme = "Forms",
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    CookieName = "auth",
    LoginPath = new PathString("/Account/Login"),
    LogoutPath = new PathString("/Account/Logout"),
    ReturnUrlParameter = "ReturnUrl"
});

プロパティ名で大体の意味は分かると思いますが、一番重要なのは AuthenticationScheme です。認証を識別するための名前となるので、この後のログインとログアウトで使います。

Startup クラスの設定はこれだけで大丈夫なので、次に実際にログインとログアウトの処理を書きます。

ログイン / ログアウトを実装

ASP.NET Core では認証系の処理は HttpContext の Authentication プロパティを経由して行います。

public class AccountController : Controller
{
    public async Task<IActionResult> Login(string returnUrl = "/")
    {
        var principal = new GenericPrincipal(new GenericIdentity("shibayan", "Forms"), null);

        await HttpContext.Authentication.SignInAsync("Forms", principal);

        return LocalRedirect(returnUrl);
    }

    public async Task<IActionResult> Logout()
    {
        await HttpContext.Authentication.SignOutAsync("Forms");

        return LocalRedirect("/");
    }
}

ここで CookieAuthentication ミドルウェアを追加できていない場合は実行時エラーになります。基本的な処理は Identity と Principal を作成し、SignInAsync メソッドを呼び出すだけです。

ログアウトはもっと簡単で SignOutAsync メソッドを呼び出すだけです。それぞれ SignInAsync と SignOutAsync メソッドに指定しているのが、AuthenticationScheme の値になります。

ユーザー情報を利用する

実際によく使われると思われる、ビュー側でログイン中ユーザーの名前を表示する処理を書いてみます。

これまでと異なり Request.IsAuthenticated プロパティは存在しないので、User.Identity の方を見てログイン中かどうかを判別するようにします。

@if (User.Identity.IsAuthenticated)
{
    <span>Hello, @User.Identity.Name</span>
}

はい、これだけです。ビューはあまり変化がないので、とても簡単でしたね。

f:id:shiba-yan:20160517171638p:plain

実際にログインを行って確認すると、ちゃんとユーザー名が表示されるようになりました。

f:id:shiba-yan:20160517171709p:plain

認証クッキーもちゃんと発行されています。これまでと変わらずビュー以外でも User プロパティが色んなところに用意されているので、簡単に使えるようになっています。

Claim を使って情報を追加する

普通は認証情報として名前だけを持たせるということはしないと思うので、Claim を使って名前以外の情報を持たせてみます。例えば Identity はユーザーの ID を持たせているので、同じことをやってみます。

さっきは GenericIdentity を使いましたが、今回は ClaimsIdentity を使うようにします。作成した ClaimsIdentity に AddClaim メソッドを使って Claim を追加していきます。

var identity = new ClaimsIdentity("Forms");

// 名前を追加
identity.AddClaim(new Claim(ClaimTypes.Name, "shibayan"));

// ID を追加
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "1"));

var principal = new ClaimsPrincipal(identity);

await HttpContext.Authentication.SignInAsync("Forms", principal);

ClaimTypes の Name と NameIdentifier を使って、名前と ID を追加してみました。

実際に User から設定した Claim を取得するには FindFirst メソッドを使います。ClaimTypes.Name で設定した内容については、これまで通り Name プロパティで取ることが出来ます。

@if (User.Identity.IsAuthenticated)
{
    <span>Id: @User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier).Value</span>
    <br />
    <span>Name: @User.Identity.Name</span>
}

これを実行してみると、ログイン後に情報が無事に表示されました。

f:id:shiba-yan:20160517173207p:plain

FormsAuthentication の場合はユーザー名のみ指定できましたが、ASP.NET Core の場合は Claim を使って情報を追加できるので、これまでより楽に扱うことが出来そうです。