Tạo cơ chế đăng nhập bằng xác thực cookie và tạo cơ chế chuyển hướng nếu bạn không được xác thực

Trang Cập Nhật :
Ngày tạo trang :

Môi trường hoạt động

Visual Studio
  • Cộng đồng Visual Studio 2022
ASP.NET Core (MVC, Razor Pages)
6.0

Lúc đầu

Lần này, ASP.NET Core sẽ sử dụng xác thực cookie làm cơ chế xác thực đăng nhập. Bạn có thể nghĩ về xác thực cookie tương tự như xác thực các hình thức truyền thống.

Một cơ chế xác thực khác cho ASP.NET Core là ASP.NET Core Identity. Ngoài việc xác thực bằng biểu mẫu, điều này cho phép bạn xác thực bằng API, sử dụng dịch vụ đăng nhập bên ngoài, quản lý và đặt lại mật khẩu, v.v. Bạn có thể sử dụng rất nhiều tính năng. Tuy nhiên, từ quan điểm chỉ tạo một màn hình đăng nhập đơn giản lần này, nó sẽ là một cơ chế xác thực có phần phóng đại. Chúng tôi sẽ không sử dụng nó lần này.

Trong các mẹo xác thực cookie được giới thiệu lần này, bạn không thể hiển thị bất kỳ thứ gì khác ngoài màn hình đăng nhập trừ khi bạn đăng nhập. Nếu bạn cố gắng điều hướng đến một màn hình khác, bạn sẽ được chuyển hướng đến màn hình đăng nhập. Nếu bạn đăng nhập, bạn có thể xem các màn hình khác.

Hiện tại, bạn có thể đăng nhập bằng cách nhập tên người dùng và mật khẩu của mình trên màn hình đăng nhập. Bản thân xác thực người dùng được thực hiện như một nơi tạm thời. Trong trường hợp này, trọng tâm chính là thực hiện xác thực cookie, vì vậy bản chất của quá trình xác định như mật khẩu có đúng hay không không phải là bản chất.

Mẹo này chỉ mô tả một phần của chương trình. Để có mã hoàn chỉnh, hãy tải xuống chương trình hoàn chỉnh. Nó cũng bao gồm cả khung MVC và Razor Pages.

Tạo dự án

Khởi động Visual Studio và tạo một dự án mới.

Đối với Razor Pages, chọn Core Web App ASP.NET hoặc đối ASP.NET với MVC, chọn Core Web App (Model-View-Controller).

Chỉ định tên dự án bạn chọn và vị trí cho dự án.

Đối với loại xác thực, chọn "Không có". Nếu bạn chọn xác thực khác, bạn sẽ sử dụng ASP.NET Core Identity. Khi bạn hoàn tất cài đặt, hãy nhấp vào nút "Tạo".

Sau khi tạo dự án, màn hình hiển thị bên dưới sẽ được hiển thị khi gỡ lỗi được thực hiện. Chúng tôi sẽ tạo một chương trình dựa trên màn hình này.

Chỉnh sửa chương trình.cs (Razor Pages, MVC Common)

Thêm các định nghĩa cần thiết để xác thực cookie vào Chương trình .cs. Các không gian tên sẽ được thêm vào như sau:

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

Đây là builder.Services mã để thêm vào .

// === 省略 ===

// 「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 Bạn có thể bật xác thực cookie bằng cách thực hiện phương thức and. Nếu bạn không cần thay đổi tên lược đồ, CookieAuthenticationDefaults.AuthenticationScheme hãy chỉ định .

AddAuthorization Nếu bạn options.FallbackPolicy RequireAuthenticatedUser chỉ định cho phương pháp Bạn có thể áp dụng chính sách yêu cầu xác thực cho tất cả các trang, tất cả các bộ điều khiển và hành động. Nếu bạn muốn yêu cầu xác thực cho bất kỳ thứ gì khác ngoài màn hình đăng nhập, đây là một phương pháp hữu ích về mặt giảm mã và ngăn ngừa lỗi trong mô tả. Bạn sẽ phải viết mã không yêu cầu xác thực cho màn hình đăng nhập riêng biệt.

Sau đây là app mã cho .

// === 省略 ===

app.UseRouting();

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

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

// === 省略 ===

Vì chúng tôi muốn thêm chức năng xác thực vào ứng dụng, app.UseAuthentication() chúng tôi sẽ thêm . Vị trí mô tả là theo tài liệu MSDN, ứng dụng. UseAuthorization(). Ngoài ra, nó vẫn là một mẫu.

Các chương trình dành cho dự án Razor Pages

Tạo trang đăng nhập (Pages/Account/Login.cshtml.cs)

Tạo tệp

Tạo trang đăng nhập. Đường dẫn tệp phải được tạo dưới dạng "/Pages/Account/Login.cshtml". Điều này là do đường dẫn đăng nhập mặc định là như vậy. Nếu bạn muốn thay đổi đường dẫn này, bạn Program.cs có thể làm như vậy bằng cách đặt đối số của AddCookie phương thức .

Bạn có thể tạo nó bằng cách sao chép các tệp khác thay vì tạo nó từ menu, nhưng trong trường hợp đó, vui lòng sửa chương trình một cách chính xác.

Cho phép người dùng truy cập trang đăng nhập mà không cần đăng nhập

Program.cs Vì tất cả các trang chỉ có thể được truy cập khi bạn đăng nhập, bạn chỉ cần đặt trang đăng nhập để bạn có thể truy cập nó ngay cả khi bạn không đăng nhập.

AllowAnonymous Bằng cách thêm các thuộc tính, trang đích có thể được truy cập ngay cả khi nó không được xác thực. AllowAnonymous Các thuộc tính có thể được sử dụng ở những nơi khác ngoài màn hình đăng nhập, chẳng hạn như các thao tác API không liên quan đến xác thực cookie.

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
  {
  }
}

Tạo một biến để nhận đầu vào

Khi bạn đăng nhập, bạn khai báo tên người dùng của mình để có thể nhận được các giá trị đó vì bạn nhập mật khẩu của mình.

// 省略

[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; } = "";
}

Xác định tên người dùng và mật khẩu để xác thực đăng nhập

Ban đầu, nó sẽ được lưu trữ trong cơ sở dữ liệu, v.v., nhưng vì đánh giá của người dùng không phải là trọng tâm chính lần này, tôi sẽ biến nó thành một nơi tạm thời.

// 省略

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

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

Quy trình đăng nhập

/// <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");
}

Đây là quá trình xác thực sau khi nhấn nút đăng nhập. Nếu tên người dùng và mật khẩu khớp nhau, có thể xác thực.

Mã được mô tả là mã tối thiểu cần thiết để xác thựcClaimClaimsIdentityClaimsPrincipalHttpContext.SignInAsync Bằng cách gọi phương thức, một cookie được tạo và xác thực.

Nếu yêu cầu bổ sung là bắt buộc hoặc hết hạn cookie, các thông số bổ sung sẽ được thêm vào.

Sau khi đăng nhập, bạn được chuyển hướng đến , nơi /Index yêu cầu xác thực.

Quy trình đăng xuất

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

Logout Khi truy cập bằng trình xử lý, nó được xử lý để đăng xuất. HttpContext.SignOutAsync Bằng cách gọi phương thức, bạn có thể xóa cookie và đưa nó trở lại trạng thái mà bạn không đăng nhập.

Tạo dạng xem

Chúng tôi không tính đến vẻ bề ngoài. Thêm các trường để nhập tên người dùng và mật khẩu của bạn như hình dưới đây và đặt nút để đăng nhập. Bạn cũng nên có một liên kết để truy cập mà không cần đăng nhập để /Index thử nghiệm.

Vì việc nhập tên người dùng và mật khẩu rất rắc rối, giá trị ban đầu được đặt.

@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"); }
}

Tạo liên kết đăng xuất (/Pages/Shared/_Layout.cshtml)

Chúng tôi sẽ không thực hiện bất kỳ thay đổi nào đối với màn hình chính, nhưng chúng tôi sẽ đặt liên kết đăng xuất vào thanh điều hướng. Ngoài ra, để thử nghiệm, hãy đăng một liên kết đưa bạn đến màn hình đăng nhập mà không cần đăng xuất.

<!-- 中略 -->
<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>
<!-- 中略 -->

Đó là nó cho mã Razor Pages.

Các chương trình cho các dự án MVC

Tạo mô hình đăng nhập

Bạn đã tạo một mô hình để nhận các giá trị được nhập trên màn hình đăng nhập.

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; } = "";
  }
}

Tạo AccountController

Tạo bộ điều khiển và hành động cần thiết để tạo màn hình đăng nhập. Tạo tên AccountController controller là . Điều này là do theo mặc định, tên bộ điều khiển và tên hành động trên màn hình đăng nhập được đặt thành "~ / Tài khoản / Đăng nhập". Nếu bạn muốn thay đổi đường dẫn này, bạn có thể làm như vậy trong các tùy chọn của AddCookie phương thức trong Program.cs. Hiện tại, chúng tôi sẽ tiến hành cài đặt mặc định.

Đầu tiên, hãy tạo phía Controller.

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 Bằng cách cấp thuộc tính, tất cả các hành động trong đó có thể được thực hiện mà không cần xác thực. Điều này cho phép bạn chỉ truy cập vào màn hình đăng nhập mà không cần xác thực.

AllowAnonymous Các thuộc tính cũng có thể được sử dụng trong các bộ điều khiển chỉ API không liên quan đến xác thực cookie ở những nơi khác ngoài màn hình đăng nhập.

Tiếp theo, xác định người dùng và mật khẩu có thể đăng nhập. Ban đầu, nó sẽ được lưu trữ trong cơ sở dữ liệu, v.v., nhưng vì đánh giá của người dùng không phải là trọng tâm chính lần này, tôi sẽ biến nó thành một nơi tạm thời.

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

Sau đây là một hành động hiển thị màn hình đăng nhập. Vì nó chỉ được hiển thị, nó trả về chế độ xem như hiện tại.

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

Dưới đây là mã sẽ được xử lý khi đăng nhập.

/// <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");
}

Đây là quá trình xác thực sau khi nhấn nút đăng nhập. Nếu tên người dùng và mật khẩu khớp nhau, có thể xác thực.

Mã được mô tả là mã tối thiểu cần thiết để xác thựcClaimClaimsIdentityClaimsPrincipalHttpContext.SignInAsync Bằng cách gọi phương thức, một cookie được tạo và xác thực.

Nếu yêu cầu bổ sung là bắt buộc hoặc hết hạn cookie, các thông số bổ sung sẽ được thêm vào.

Sau khi đăng nhập, bạn được chuyển hướng đến , nơi ~/Home/Index yêu cầu xác thực.

Cuối cùng, quá trình đăng xuất được thêm vào.

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

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

HttpContext.SignOutAsync Bằng cách gọi phương thức, bạn có thể xóa cookie và đưa nó trở lại trạng thái mà bạn không đăng nhập.

Tạo dạng xem (biểu mẫu đăng nhập) (/Views/Account/Login.cshtml)

Login Bấm chuột phải vào hành động để thêm dạng xem. Bạn cũng có thể tạo nó bằng cách sao chép nó từ một tệp khác.

Chúng tôi không tính đến vẻ bề ngoài. Thêm các trường để nhập tên người dùng và mật khẩu của bạn như hình dưới đây và đặt nút để đăng nhập. Bạn cũng nên có một liên kết để truy cập mà không cần đăng nhập để Home/Index thử nghiệm.

Vì việc nhập tên người dùng và mật khẩu rất rắc rối, giá trị ban đầu được đặt.

@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"); }
}

Tạo liên kết đăng xuất (/Views/Shared/_Layout.cshtml)

Chúng tôi sẽ không thực hiện bất kỳ thay đổi nào đối với màn hình chính, nhưng chúng tôi sẽ đặt liên kết đăng xuất vào thanh điều hướng. Ngoài ra, để thử nghiệm, hãy đăng một liên kết đưa bạn đến màn hình đăng nhập mà không cần đăng xuất.

<!-- 中略 -->
<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>
<!-- 中略 -->

Đó là nó cho mã MVC.

Xác nhận hoạt động

Điều này hoàn thành việc thực hiện xác thực cookie bắt buộc tối thiểu. Hãy thử chạy nó và xem nó hoạt động như thế nào. Hành vi sẽ thay đổi tùy thuộc vào việc bạn có đăng nhập hay không. Một ví dụ đơn giản, bạn có thể thấy hành vi sau đây.

động
Kết quả hoạt hoạt động
Về nhà mà không cần đăng nhập Chuyển hướng đến màn hình đăng nhập
Đăng nhập Chuyển đến màn hình chính
Đăng xuất khỏi nhà và về nhà mà không cần đăng nhập Chuyển hướng đến màn hình đăng nhập
Về nhà mà không cần đăng xuất khỏi nhà và đăng nhập Chuyển đến màn hình chính