ASP.NET CoreでCookie認証を使ってログイン・ログアウト機能を実装してみます。
Microsoft.AspNetCore.Authentication
を使うと簡単に行うと簡単に実装できるようです。
[Microsoft.AspNetCore.Authentication - Nuget]
.NET Core SDKのバージョンは以下の通りです。
$ dotnet --version
2.1.502
サンプルプロジェクトの作成
今回はRazorページのテンプレートを使いたいと思います。Visual Studioでは以下の操作でプロジェクトを開始できます。
新規作成
> プロジェクト
>新しいプロジェクト
> Web
> ASP.NET Core Web アプリケーション
> Web アプリケーション
.NET Core CLIで作成する場合は以下のコマンドです。
$ dotnet new razor -n "プロジェクト名"
完成後のソースはこちらのリポジトリに置きました。
https://github.com/sano-suguru/netcore-app-with-cookie-auth
ところどころ端折っているので実際のコードは上記のリンクからご確認ください。
クッキー認証ミドルウェアの設定
Startup.cs
に認証ミドルウェアを追加します。
その際、Cookieに、セッションハイジャック等を防ぐためにhttpOnly
属性(JSから触れなくする)、Secure
属性(SSL時のみクッキーを送信)を設定します。
ConfigureServices
メソッドに以下の通り設定を追加。
services.Configure<CookiePolicyOptions>(options => {
options.CheckConsentNeeded = context => !context.User.Identity.IsAuthenticated;
options.MinimumSameSitePolicy = SameSiteMode.None;
+ options.Secure = CookieSecurePolicy.Always;
+ options.HttpOnly = HttpOnlyPolicy.Always;
});
+ services
+ .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
+ .AddCookie();
Configure
メソッドに以下の通り設定を追加。
app.UseHttpsRedirection();
app.UseStaticFiles();
+ app.UseCookiePolicy();
+ app.UseAuthentication();
app.UseMvc();
}
ログインページ、ログイン処理を作成する
/Pages/Account/
ディレクトリにLogin.cshtml
とLogin.cshtml.cs
を作成します。
ログインが必要なページに未ログイン状態でアクセスした場合に自動でリダイレクトされるデフォルトのパスが/Account/Login
であるためです。
@page
@model LoginModel
@{
ViewData["Title"] = "Login";
}
<h2>Login</h2>
<form method="post">
<div class="form-group">
<label asp-for="Input.Email">Email address</label>
<input asp-for="Input.Email" class="form-control" placeholder="Enter email">
</div>
<div class="form-group">
<label asp-for="Input.Password">Password</label>
<input asp-for="Input.Password" class="form-control" placeholder="Password">
</div>
<div class="form-check">
<input asp-for="Input.RememberMe" class="form-check-input">
<label asp-for="Input.RememberMe" class="form-check-label">Remember Me</label>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
cshtml
ではタグヘルパーasp-for
でLoginModel
クラスのインナークラスInputModel
にフォームの値をバインドしています。
[BindProperty]
public InputModel Input { get; set; }
public class InputModel {
[Required]
[EmailAddress]
public string Email { get; set; }
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Display(Name = "Remember me?")]
public bool RememberMe { get; set; }
}
以下、Login.cshtml.cs
のLoginModel
クラスからログイン処理を抜粋します。
サンプルのため処理べた書きです。実際にはDBアクセス処理やエラー表示が必要になると思います。
// submitボタンがクリックされてpostリクエストを受け付けたときに動く
public async Task<IActionResult> OnPostAsync(string returnUrl = null) {
// 必須入力がないなどの場合ログインさせない。(ログインページに戻る)
if (!ModelState.IsValid) return Page();
// ==================================================
// ダミーの認証処理(実際にはDBを使います)
var mockDB = new[] {
(email: "nossa@example.com", password: "pazzword1"),
(email: "nyaan@example.com", password: "pazzword2")
};
bool isValid = mockDB.SingleOrDefault(x => x.email == Input.Email && x.password == Input.Password) != null;
// ===================================================
// Eメール・パスワードが間違っている場合ログインさせない。
if (!isValid) return Page();
// ★以下ログイン処理
// 名前、電子メール アドレス、年齢、Sales ロールのメンバーシップなど、id 情報の一部
Claim[] claims = {
new Claim(ClaimTypes.NameIdentifier, Input.Email), // ユニークID
new Claim(ClaimTypes.Name, Input.Email),
};
// 一意の ID 情報
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
// ログイン
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties {
// Cookie をブラウザー セッション間で永続化するか?(ブラウザを閉じてもログアウトしないかどうか)
IsPersistent = Input.RememberMe
});
return LocalRedirect(returnUrl ?? Url.Content("~/"));
}
ログアウトページを作成する
@page
@model AuthWithCookie.Pages.Account.LogoutModel
@{
ViewData["Title"] = "Logout";
}
<h2>Logout</h2>
<form method="post">
<button type="submit" class="btn btn-primary">Log out</button>
</form>
ログアウトは本当に簡単でHttpContext.SignOutAsync
を呼ぶだけです。
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.Threading.Tasks;
namespace AuthWithCookie.Pages.Account {
public class LogoutModel : PageModel {
public async Task<IActionResult> OnPostAsync() {
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
// ログアウト後はトップページへリダイレクト
return LocalRedirect(Url.Content("~/"));
}
}
}
ナビゲーションバーにログイン・ログアウトページを登録する
ログイン・ログアウトページにナビゲーションバーからアクセスできるようにします。
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a asp-page="/Index">Home</a></li>
<li><a asp-page="/About">About</a></li>
<li><a asp-page="/Contact">Contact</a></li>
+ <li><a asp-page="/Account/Login">Log in</a></li>
+ <li><a asp-page="/Account/Logout">Log out</a></li>
</ul>
</div>
ログイン済みでないと閲覧できないページを作る
Cotact
ページをログイン者専用のコンテンツにしてみます。
[Authorize]
属性をコントローラークラスに付けるとログイン済みでないとそのコントローラーにアクセスできなくなります。また、アクションメソッドごとに個別に付けることも可能です。
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace AuthWithCookie.Pages {
[Authorize]
public class ContactModel : PageModel {
public string Message { get; set; }
public void OnGet() => Message = "Your contact page.";
}
}
ログインしていない状態でナビゲーションバーのContactをクリック(/Contact
にアクセス)するとログインページ/Account/Login
に自動でリダイレクトします。
ログインするとログインページに直接アクセスした場合はトップへ、未ログイン状態でログインが必要なページにアクセスしログインページにリダイレクトされた場合にはそのページへ戻ります。
return LocalRedirect(returnUrl ?? Url.Content("~/"));
ログイン状態では/Contact
ページを閲覧できます。