使用cookie身份驗證創建登錄機制,並創建在未通過身份驗證時重定向的機制

更新頁 :
頁面創建日期 :

操作環境

Visual Studio的
  • Visual Studio 社區 2022
ASP.NET Core (MVC, Razor Pages)
6.0

起先

這一次,ASP.NET Core將使用 cookie身份驗證作為登錄身份驗證機制。 您可以將 Cookie 身份驗證視為類似於傳統的表單身份驗證。

ASP.NET Core 的另一種身份驗證機制是 ASP.NET Core Identity。 除了使用表單進行身份驗證外,它還允許您使用 API 進行身份驗證、使用外部登錄服務、管理和重置密碼等。 您可以使用很多功能。 但是,從這次僅創建一個簡單的登錄螢幕的角度來看,這將是一種有些誇張的身份驗證機制。 這次我們不會使用它。

在這次介紹的cookie認證提示中,除非您登錄,否則您不能顯示登錄螢幕以外的任何內容。 如果您嘗試導航到另一個螢幕,您將被重定向到登錄螢幕。 如果登錄,則可以查看其他螢幕。

目前,您可以通過在登錄螢幕上輸入使用者名和密碼來登錄。 使用者身份驗證本身是作為臨時位置實現的。 在這種情況下,主要關注點是cookie身份驗證的實現,因此密碼是否正確等確定過程的本質並不是本質。

本提示僅介紹程式的一部分。 如需完整代碼,請下載完整程式。 它還涵蓋MVC和Razor Pages框架。

創建專案

啟動 Visual Studio 並創建一個新專案。

對於Razor Pages,請選擇“ASP.NET 核心Web應用”,對於MVC,請選擇“ASP.NET 核心Web應用(模型-視圖-控制器)”。

指定您選擇的項目名稱和專案的位置。

對於身份驗證類型,請選擇“無”。 如果選擇其他身份驗證,則將使用 ASP.NET Core Identity。 完成設置後,按兩下「創建」 按鈕。

創建專案后,執行調試時將顯示如下所示的螢幕。 我們將基於此螢幕創建一個程式。

編輯程式 .cs(剃刀頁面、MVC 通用)

將 Cookie 身份驗證所需的定義添加到 Program.cs。 要新增的命名空間如下:

using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;

下面是要添加到的 builder.Services 代碼。

// === 省略 ===

// 「Razor Pages」のコード
// builder.Services.AddRazorPages();
// 「MVC」のコード
// builder.Services.AddControllersWithViews();

// ※ここから追加

// Cookie による認証スキームを追加する
builder.Services
  .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
  .AddCookie();

builder.Services.AddAuthorization(options =>
{
  // AllowAnonymous 属性が指定されていないすべての画面、アクションなどに対してユーザー認証が必要となる
  options.FallbackPolicy = new AuthorizationPolicyBuilder()
    .RequireAuthenticatedUser()
    .Build();
});

// ※ここまで追加

var app = builder.Build();

// === 省略 ===

AddAuthenticationAddCookie您可以通過執行 and 方法啟用 cookie 身份驗證。 如果不需要變更方案名稱,CookieAuthenticationDefaults.AuthenticationScheme請指定 。

AddAuthorizationoptions.FallbackPolicy RequireAuthenticatedUser如果指定 for 方法 您可以將需要身份驗證的策略應用於所有頁面、所有控制器和操作。 如果您想要求對登錄螢幕以外的任何內容進行身份驗證,那麼在減少代碼和防止描述錯誤方面,這是一種有用的方法。 您必須單獨編寫不需要對登錄螢幕進行身份驗證的代碼。

以下是 app 的代碼。

// === 省略 ===

app.UseRouting();

app.UseAuthentication(); // [追加] 認証
app.UseAuthorization(); // 認可

// 「Razor Pages」のコード
// app.MapRazorPages();
// 「MVC」のコード
// app.MapControllerRoute(
//     name: "default",
//     pattern: "{controller=Home}/{action=Index}/{id?}");

// === 省略 ===

由於我們要向應用程式添加身份驗證功能, app.UseAuthentication() 因此我們將添加 . 描述位置根據 MSDN 文件 app。 UseAuthorization() 中。 除此之外,它仍然是一個範本。

Razor Pages 項目的程式

建立登入頁(頁面/帳戶/登錄.cshtml.cs)

創建檔案

創建登錄頁面。 文件路徑應創建為“/Pages/Account/Login.cshtml”。 這是因為默認登錄路徑就是這樣。 如果要更改此路徑, Program.cs 可以通過設置 方法的 AddCookie 參數來實現。

您可以通過複製其他檔而不是從功能表中創建它來創建它,但在這種情況下,請正確修復程式。

允許使用者在不登錄的情況下訪問登錄頁面

Program.cs 由於所有頁面只有在登錄時才能訪問,因此您只需要設置登錄頁面,以便即使您沒有登錄也可以訪問它。

AllowAnonymous 通過添加屬性,即使目標頁面未經身份驗證,也可以訪問該頁面。 AllowAnonymous 屬性可以在登錄螢幕以外的其他地方使用,例如與 cookie 身份驗證無關的 API 操作。

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Security.Claims;

namespace AspNetCoreCookieAuthenticationRazorPages.Pages.Account
{
  [AllowAnonymous]
  public class LoginModel : PageModel
  {
  }
}

創建用於接收輸入的變數

當您登錄時,您聲明您的使用者名能夠接收這些值,因為您輸入了密碼。

// 省略

[AllowAnonymous]
public class LoginModel : PageModel
{
  /// <summary>ユーザー名。</summary>
  [BindProperty]
  [Required]
  [DisplayName("ユーザー名")]
  public string UserName { get; set; } = "";

  /// <summary>パスワード。</summary>
  [BindProperty]
  [Required]
  [DataType(DataType.Password)]
  [DisplayName("パスワード")]
  public string Password { get; set; } = "";
}

定義用於登錄身份驗證的使用者名和密碼

最初,它會存儲在資料庫等中,但由於這次不是主要關注點,因此我將將其作為臨時位置。

// 省略

[AllowAnonymous]
public class LoginModel : PageModel
{
  // 省略

  /// <summary>仮のユーザーデータベースとする。</summary>
  private Dictionary<string, string> UserAccounts { get; set; } = new Dictionary<string, string>
    {
      { "user1", "password1" },
      { "user2", "password2" },
    };
}

登錄過程

/// <summary>ログイン処理。</summary>
public async Task<ActionResult> OnPost()
{
  // 入力内容にエラーがある場合は処理を中断してエラー表示
  if (ModelState.IsValid == false) return Page();

  // ユーザーの存在チェックとパスワードチェック (仮実装)
  // 本 Tips は Cookie 認証ができるかどうかの確認であるため入力内容やパスワードの厳密なチェックは行っていません
  if (UserAccounts.TryGetValue(UserName, out string? getPass) == false || Password != getPass)
  {
    ModelState.AddModelError("", "ユーザー名またはパスワードが一致しません。");
    return Page();
  }

  // サインインに必要なプリンシパルを作る
  var claims = new[] { new Claim(ClaimTypes.Name, UserName) };
  var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
  var principal = new ClaimsPrincipal(identity);

  // 認証クッキーをレスポンスに追加
  await HttpContext.SignInAsync(principal);

  // ログインが必要な画面にリダイレクトします
  return RedirectToPage("/Index");
}

這是按下登錄按鈕后的身份驗證過程。 如果使用者名和密碼匹配,則可以進行身份驗證。

所描述的代碼是身份驗證所需的最小代碼,ClaimClaimsIdentityClaimsPrincipal並且 HttpContext.SignInAsync通過調用該方法,將生成一個cookie並對其進行身份驗證。

如果需要其他聲明或需要 Cookie 過期,則會添加其他參數。

登錄后,系統會將您重定向到 ,其中 /Index 需要身份驗證。

註銷過程

/// <summary>ログアウト処理。</summary>
public async Task OnGetLogout()
{
  // 認証クッキーをレスポンスから削除
  await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
}

Logout 當使用處理程式訪問時,將對其進行處理以註銷。 HttpContext.SignOutAsync 通過調用該方法,您可以刪除cookie並將其恢復到未登錄的狀態。

創建檢視

我們不考慮外觀。 如下圖所示,添加用於輸入使用者名和密碼的欄位,然後放置一個登錄按鈕。 您還應該有一個連結,無需登錄即可 /Index 訪問測試。

由於輸入使用者名和密碼很麻煩,因此設置了初始值。

@page
@model AspNetCoreCookieAuthenticationRazorPages.Pages.Account.LoginModel
@{}

<form asp-action="Login">
  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    </div>
  </div>

  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <label asp-for="UserName" class="form-label"></label>
      <input asp-for="UserName" class="form-control" value="user1" />
      <span asp-validation-for="UserName" class="text-danger"></span>
    </div>
  </div>
  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <label asp-for="Password" class="form-label"></label>
      <input asp-for="Password" class="form-control" value="password1" />
      <span asp-validation-for="Password" class="text-danger"></span>
    </div>
  </div>
  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <button type="submit" class="btn btn-primary">ログイン</button>
    </div>
  </div>
  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <a asp-page="/Index">認証が必要な画面へ直接リンク</a>
    </div>
  </div>
</form>

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

創建註銷連結 (/Pages/Shared/_Layout.cshtml)Create a logout link (/Pages/Shared/.cshtml)

我們不會對主螢幕進行任何更改,但我們會在導航欄中放置一個註銷連結。 此外,為了進行測試,請發佈一個連結,無需註銷即可進入登錄螢幕。

<!-- 中略 -->
<div class="navbar-collapse collapse d-sm-inline-flex justify-content-between">
  <ul class="navbar-nav flex-grow-1">
    <li class="nav-item">
      <a class="nav-link text-dark" asp-area="" asp-page="/Index">Home</a>
    </li>
    <li class="nav-item">
      <a class="nav-link text-dark" asp-area="" asp-page="/Privacy">Privacy</a>
    </li>
    <!-- ここから追加 -->
    <li class="nav-item">
      <a class="nav-link text-dark" asp-page="/Account/Login" asp-page-handler="Logout">ログアウト</a>
    </li>
    <li class="nav-item">
      <a class="nav-link text-dark" asp-page="/Account/Login">ログアウトせずログインへ</a>
    </li>
    <!-- ここまで追加 -->
  </ul>
</div>
<!-- 中略 -->

這就是 Razor Pages 代碼的內容。

MVC 專案程式

創建登錄模型

您已建立一個模型來接收在登錄螢幕上輸入的值。

namespace AspNetCoreCookieAuthenticationMvc.Models
{
  public class LoginModel
  {
    /// <summary>ユーザー名。</summary>
    [Required]
    [DisplayName("ユーザー名")]
    public string UserName { get; set; } = "";

    /// <summary>パスワード。</summary>
    [Required]
    [DataType(DataType.Password)]
    [DisplayName("パスワード")]
    public string Password { get; set; } = "";
  }
}

創建 AccountController

創建創建登錄螢幕所需的控制器和操作。 將控制器名稱 AccountController 建立為 。 這是因為默認情況下,登錄螢幕上的控制器名稱和操作名稱設置為“~/Account/Login”。 如果要更改此路徑,可以在 Program.cs 中方法的 AddCookie 選項中執行此操作。 現在,我們將繼續使用預設設置。

首先,讓我們創建控制器端。

using AspNetCoreCookieAuthenticationMvc.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Security.Claims;

namespace AspNetCoreCookieAuthenticationMvc.Controllers
{
  /// <remarks>
  /// <see cref="AllowAnonymous"/> 属性は Cookie 認証していなくてもアクセスできる Action (Controller) であることを示す。
  /// </remarks>
  [AllowAnonymous]
  public class AccountController : Controller
  {
  }
}

AllowAnonymous 通過授予該屬性,可以在不經過身份驗證的情況下執行其中的所有操作。 這允許您在沒有身份驗證的情況下僅訪問登錄螢幕。

AllowAnonymous 屬性也可用於僅 API 控制器,這些控制器與登錄螢幕以外的其他地方的 cookie 身份驗證無關。

接下來,定義可以登錄的用戶和密碼。 最初,它會存儲在資料庫等中,但由於這次不是主要關注點,因此我將將其作為臨時位置。

[AllowAnonymous]
public class AccountController : Controller
{
  /// <summary>仮のユーザーデータベースとする。</summary>
  private Dictionary<string, string> UserAccounts { get; set; } = new Dictionary<string, string>
    {
      { "user1", "password1" },
      { "user2", "password2" },
    };
}

以下是顯示登錄螢幕的操作。 由於它僅顯示,因此它按原樣返回視圖。

/// <summary>ログイン画面を表示します。</summary>
public IActionResult Login() => View();

以下是登錄時要處理的代碼。

/// <summary>ログイン処理を実行します。</summary>
[HttpPost]
public async Task<IActionResult> Login(LoginModel model)
{
  // 入力内容にエラーがある場合は処理を中断してエラー表示
  if (ModelState.IsValid == false) return View(model);

  // ユーザーの存在チェックとパスワードチェック (仮実装)
  // 本 Tips は Cookie 認証ができるかどうかの確認であるため入力内容やパスワードの厳密なチェックは行っていません
  if (UserAccounts.TryGetValue(model.UserName, out string? getPass) == false || model.Password != getPass)
  {
    ModelState.AddModelError("", "ユーザー名またはパスワードが一致しません。");
    return View(model);
  }

  // サインインに必要なプリンシパルを作る
  var claims = new[] { new Claim(ClaimTypes.Name, model.UserName) };
  var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
  var principal = new ClaimsPrincipal(identity);

  // 認証クッキーをレスポンスに追加
  await HttpContext.SignInAsync(principal);

  // ログインが必要な画面にリダイレクトします
  return RedirectToAction(nameof(HomeController.Index), "Home");
}

這是按下登錄按鈕后的身份驗證過程。 如果使用者名和密碼匹配,則可以進行身份驗證。

所描述的代碼是身份驗證所需的最小代碼,ClaimClaimsIdentityClaimsPrincipal並且 HttpContext.SignInAsync通過調用該方法,將生成一個cookie並對其進行身份驗證。

如果需要其他聲明或需要 Cookie 過期,則會添加其他參數。

登錄后,系統會將您重定向到 ,其中 ~/Home/Index 需要身份驗證。

最後,添加註銷過程。

/// <summary>ログアウト処理を実行します。</summary>
public async Task<IActionResult> Logout()
{
  // 認証クッキーをレスポンスから削除
  await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

  // ログイン画面にリダイレクト
  return RedirectToAction(nameof(Login));
}

HttpContext.SignOutAsync 通過調用該方法,您可以刪除cookie並將其恢復到未登錄的狀態。

創建視圖(登錄窗體)(/Views/Account/Login.cshtml)Create a view (login form) (/Views/Account/Login.cshtml)

Login 右鍵按下該操作以添加檢視。 您也可以通過從另一個檔案複製它來創建它。

我們不考慮外觀。 如下圖所示,添加用於輸入使用者名和密碼的欄位,然後放置一個登錄按鈕。 您還應該有一個連結,無需登錄即可 Home/Index 訪問測試。

由於輸入使用者名和密碼很麻煩,因此設置了初始值。

@model LoginModel
@{}

<form asp-action="Login">
  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <div asp-validation-summary="ModelOnly" class="text-danger"></div>
    </div>
  </div>

  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <label asp-for="UserName" class="form-label"></label>
      <input asp-for="UserName" class="form-control" value="user1" />
      <span asp-validation-for="UserName" class="text-danger"></span>
    </div>
  </div>
  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <label asp-for="Password" class="form-label"></label>
      <input asp-for="Password" class="form-control" value="password1" />
      <span asp-validation-for="Password" class="text-danger"></span>
    </div>
  </div>
  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <button type="submit" class="btn btn-primary">ログイン</button>
    </div>
  </div>
  <div class="row m-1 g-3">
    <div class="col-sm-6 offset-sm-3">
      <a asp-controller="Home" asp-action="Index">認証が必要な画面へ直接リンク</a>
    </div>
  </div>
</form>

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

創建註銷連結 (/Views/Shared/_Layout.cshtml)Create a logout link (/Views/Shared/.cshtml)

我們不會對主螢幕進行任何更改,但我們會在導航欄中放置一個註銷連結。 此外,為了進行測試,請發佈一個連結,無需註銷即可進入登錄螢幕。

<!-- 中略 -->
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse">
  <ul class="navbar-nav flex-grow-1">
    <li class="nav-item">
      <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Index">Home</a>
    </li>
    <li class="nav-item">
      <a class="nav-link text-dark" asp-area="" asp-controller="Home" asp-action="Privacy">Privacy</a>
    </li>
    <!-- ここから追加 -->
    <li class="nav-item">
      <a class="nav-link text-dark" asp-controller="Account" asp-action="Logout">ログアウト</a>
    </li>
    <li class="nav-item">
      <a class="nav-link text-dark" asp-controller="Account" asp-action="Login">ログアウトせずログインへ</a>
    </li>
    <!-- ここまで追加 -->
  </ul>
</div>
<!-- 中略 -->

MVC 代碼就是這樣。

操作確認

這樣就完成了 Cookie 身份驗證的最低要求實現。 嘗試運行它,看看它是如何工作的。 該行為應根據您是否登錄而更改。 舉個簡單的例子,您可以看到以下行為。

操作 操作結果
無需登錄即可回家 重定向至登錄螢幕
登錄 轉到主螢幕
退出家中,無需登錄即可回家 重定向至登錄螢幕
無需註銷和登錄即可回家 轉到主螢幕