使用 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 身份验证的最低要求实现。 尝试运行它,看看它是如何工作的。 该行为应根据您是否登录而更改。 举个简单的例子,您可以看到以下行为。

操作 操作结果
无需登录即可回家 重定向至登录屏幕
登录 转到主屏幕
退出家中,无需登录即可回家 重定向至登录屏幕
无需注销和登录即可回家 转到主屏幕