Buat mekanisme log masuk menggunakan pengesahan kuki, dan buat mekanisme yang akan diarahkan semula jika anda tidak disahkan

Laman dikemaskini :
Tarikh penciptaan halaman :

Persekitaran operasi

Visual Studio
  • Komuniti Visual Studio 2022
Teras ASP.NET (MVC, Halaman Cukur)
6.0

Pada mulanya

Kali ini, ASP.NET Core akan menggunakan pengesahan kuki sebagai mekanisme pengesahan log masuk. Anda boleh menganggap pengesahan kuki sebagai serupa dengan pengesahan borang tradisional.

Satu lagi mekanisme pengesahan untuk ASP.NET Core ialah ASP.NET Identiti Teras. Selain pengesahan menggunakan borang, ini membolehkan anda mengesahkan dengan API, menggunakan perkhidmatan log masuk luaran, mengurus dan menetapkan semula kata laluan, dll. Anda boleh menggunakan banyak ciri. Walau bagaimanapun, dari sudut pandangan hanya membuat skrin log masuk mudah kali ini, ia akan menjadi mekanisme pengesahan yang agak dibesar-besarkan. Kami tidak akan menggunakannya kali ini.

Dalam petua pengesahan kuki yang diperkenalkan kali ini, anda tidak boleh memaparkan apa-apa selain daripada skrin log masuk melainkan anda log masuk. Jika anda cuba menavigasi ke skrin lain, anda akan diarahkan ke skrin log masuk. Jika anda log masuk, anda boleh melihat skrin lain.

Buat masa ini, anda boleh log masuk dengan memasukkan nama pengguna dan kata laluan anda pada skrin log masuk. Pengesahan pengguna itu sendiri dilaksanakan sebagai tempat sementara. Dalam kes ini, tumpuan utama adalah pada pelaksanaan pengesahan kuki, jadi intipati proses penentuan seperti sama ada kata laluan itu betul bukan intipati.

Petua ini menerangkan hanya sebahagian daripada program. Untuk kod lengkap, muat turun program lengkap. Ia juga merangkumi rangka kerja MVC dan Razor Pages.

Mencipta projek

Mulakan Visual Studio dan buat projek baharu.

Untuk Halaman Cukur, pilih ASP.NET Aplikasi Web Teras, atau untuk MVC, pilih ASP.NET Aplikasi Web Teras (Model-View-Controller).

Tentukan nama projek pilihan anda dan lokasi untuk projek.

Untuk jenis pengesahan, pilih "Tiada". Jika anda memilih pengesahan lain, anda akan menggunakan ASP.NET Identiti Teras. Apabila anda selesai dengan tetapan, klik butang "Buat".

Selepas membuat projek, skrin yang ditunjukkan di bawah akan dipaparkan apabila penyahpepijatan dilaksanakan. Kami akan membuat program berdasarkan skrin ini.

Edit Program.cs (Halaman Cukur, MVC Common)

Tambah definisi yang diperlukan untuk pengesahan kuki kepada Program.cs. Ruang nama yang akan ditambah adalah seperti berikut:

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

Berikut ialah builder.Services kod untuk ditambahkan pada .

// === 省略 ===

// 「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 Anda boleh mendayakan pengesahan kuki dengan melaksanakan kaedah dan kaedah. Jika anda tidak perlu menukar nama skema, CookieAuthenticationDefaults.AuthenticationScheme tentukan .

AddAuthorization Jika anda options.FallbackPolicy RequireAuthenticatedUser menentukan kaedah tersebut Anda boleh menggunakan dasar yang diperlukan pengesahan pada semua halaman, semua pengawal dan tindakan. Jika anda ingin memerlukan pengesahan untuk apa-apa selain daripada skrin log masuk, ia adalah kaedah yang berguna dari segi mengurangkan kod dan mencegah kesilapan dalam keterangan. Anda perlu menulis kod yang tidak memerlukan pengesahan untuk skrin log masuk secara berasingan.

Berikut adalah app kod untuk .

// === 省略 ===

app.UseRouting();

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

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

// === 省略 ===

Oleh kerana kami ingin menambah fungsi pengesahan pada aplikasi, app.UseAuthentication() kami akan menambah . Lokasi penerangan adalah mengikut dokumentasi MSDN, aplikasi. UseAuthorization(). Selain daripada itu, ia masih templat.

Program untuk projek Halaman Pisau Cukur

Buat halaman log masuk (Pages/Account/Login.cshtml.cs)

Mencipta fail

Buat halaman log masuk. Laluan fail hendaklah dibuat sebagai "/Pages/Account/Login.cshtml". Ini kerana laluan log masuk lalai seperti itu. Jika anda ingin menukar laluan ini, anda Program.cs boleh berbuat demikian dengan menetapkan hujah kaedah .AddCookie

Anda boleh membuatnya dengan menyalin fail lain dan bukannya menciptanya dari menu, tetapi dalam kes itu, sila betulkan program dengan betul.

Benarkan pengguna mengakses halaman log masuk tanpa log masuk

Program.cs Oleh kerana semua halaman hanya boleh diakses semasa anda log masuk, anda hanya perlu menetapkan halaman log masuk supaya anda boleh mengaksesnya walaupun anda tidak log masuk.

AllowAnonymous Dengan menambah atribut, halaman sasaran boleh diakses walaupun ia tidak disahkan. AllowAnonymous Atribut boleh digunakan di tempat lain selain daripada skrin log masuk, seperti operasi API yang tidak berkaitan dengan pengesahan kuki.

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

Mencipta pemboleh ubah untuk menerima input

Apabila anda log masuk, anda mengisytiharkan nama pengguna anda untuk dapat menerima nilai tersebut kerana anda memasukkan kata laluan anda.

// 省略

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

Tentukan nama pengguna dan kata laluan untuk pengesahan log masuk

Pada asalnya, ia akan disimpan dalam pangkalan data, dan lain-lain, tetapi kerana pertimbangan pengguna bukanlah fokus utama kali ini, saya akan menjadikannya sebagai tempat sementara.

// 省略

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

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

Proses log masuk

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

Ini adalah proses pengesahan selepas menekan butang log masuk. Jika nama pengguna dan kata laluan sepadan, pengesahan adalah mungkin.

Kod yang diterangkan adalah kod minimum yang diperlukan untuk pengesahan,ClaimClaimsIdentityClaimsPrincipal dan HttpContext.SignInAsync Dengan memanggil kaedah ini, kuki dijana dan disahkan.

Jika tuntutan tambahan diperlukan atau tamat tempoh kuki diperlukan, parameter tambahan ditambah.

Selepas log masuk, anda diarahkan ke , di mana /Index pengesahan diperlukan.

Proses log keluar

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

Logout Apabila diakses dengan pengendali, ia diproses untuk log keluar. HttpContext.SignOutAsync Dengan memanggil kaedah, anda boleh memadam kuki dan mengembalikannya ke keadaan di mana anda tidak log masuk.

Mencipta Pandangan

Kami tidak mengambil kira penampilan. Tambah medan untuk memasukkan nama pengguna dan kata laluan anda seperti yang ditunjukkan di bawah, dan letakkan butang untuk log masuk. Anda juga harus mempunyai pautan untuk mengakses tanpa log masuk untuk /Index ujian.

Oleh kerana menyusahkan untuk memasukkan nama pengguna dan kata laluan, nilai awal ditetapkan.

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

Cipta pautan log keluar (/Pages/Shared/_Layout.cshtml)

Kami tidak akan membuat sebarang perubahan pada skrin utama, tetapi kami akan meletakkan pautan log keluar dalam bar navigasi. Juga, untuk ujian, siarkan pautan yang membawa anda ke skrin log masuk tanpa log keluar.

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

Itu sahaja untuk kod Halaman Cukur.

Program untuk Projek MVC

Mencipta Model Log Masuk

Anda telah mencipta model untuk menerima nilai yang dimasukkan pada skrin log masuk.

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

Membuat AccountController

Buat pengawal dan tindakan yang diperlukan untuk membuat skrin log masuk. Buat nama AccountController pengawal sebagai . Ini kerana secara lalai, nama pengawal dan nama tindakan pada skrin log masuk ditetapkan kepada "~/Account/Login". Jika anda ingin menukar laluan ini, anda boleh berbuat demikian dalam pilihan AddCookie kaedah dalam Program.cs. Buat masa ini, kami akan meneruskan tetapan lalai.

Pertama, mari kita buat bahagian Pengawal.

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 Dengan memberikan atribut, semua tindakan di dalamnya boleh dilakukan tanpa disahkan. Ini membolehkan anda mengakses skrin log masuk sahaja tanpa pengesahan.

AllowAnonymous Atribut juga boleh digunakan dalam pengawal API sahaja yang tidak berkaitan dengan pengesahan kuki di tempat lain selain daripada skrin log masuk.

Seterusnya, tentukan pengguna dan kata laluan yang boleh log masuk. Pada asalnya, ia akan disimpan dalam pangkalan data, dan lain-lain, tetapi kerana pertimbangan pengguna bukanlah fokus utama kali ini, saya akan menjadikannya sebagai tempat sementara.

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

Berikut adalah tindakan yang memaparkan skrin log masuk. Oleh kerana ia hanya dipaparkan, ia mengembalikan pandangan seperti sedia ada.

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

Berikut adalah kod yang akan diproses semasa log masuk.

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

Ini adalah proses pengesahan selepas menekan butang log masuk. Jika nama pengguna dan kata laluan sepadan, pengesahan adalah mungkin.

Kod yang diterangkan adalah kod minimum yang diperlukan untuk pengesahan,ClaimClaimsIdentityClaimsPrincipal dan HttpContext.SignInAsync Dengan memanggil kaedah ini, kuki dijana dan disahkan.

Jika tuntutan tambahan diperlukan atau tamat tempoh kuki diperlukan, parameter tambahan ditambah.

Selepas log masuk, anda diarahkan ke , di mana ~/Home/Index pengesahan diperlukan.

Akhirnya, proses log keluar ditambah.

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

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

HttpContext.SignOutAsync Dengan memanggil kaedah, anda boleh memadam kuki dan mengembalikannya ke keadaan di mana anda tidak log masuk.

Mencipta paparan (borang log masuk) (/Views/Account/Login.cshtml)

Login Klik kanan tindakan untuk menambah pandangan. Anda juga boleh menciptanya dengan menyalinnya dari fail lain.

Kami tidak mengambil kira penampilan. Tambah medan untuk memasukkan nama pengguna dan kata laluan anda seperti yang ditunjukkan di bawah, dan letakkan butang untuk log masuk. Anda juga harus mempunyai pautan untuk mengakses tanpa log masuk untuk Home/Index ujian.

Oleh kerana menyusahkan untuk memasukkan nama pengguna dan kata laluan, nilai awal ditetapkan.

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

Cipta pautan log keluar (/Views/Shared/_Layout.cshtml)

Kami tidak akan membuat sebarang perubahan pada skrin utama, tetapi kami akan meletakkan pautan log keluar dalam bar navigasi. Juga, untuk ujian, siarkan pautan yang membawa anda ke skrin log masuk tanpa log keluar.

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

Itu sahaja untuk kod MVC.

Pengesahan operasi

Ini melengkapkan pelaksanaan minimum pengesahan kuki yang diperlukan. Cuba jalankannya dan lihat bagaimana ia berfungsi. Tingkah laku harus berubah bergantung pada sama ada anda log masuk atau tidak. Sebagai contoh mudah, anda boleh melihat kelakuan berikut.

Keputusan operasi
operasi
Pulang tanpa log masuk Ubah hala ke skrin log masuk
Log masuk Pergi ke skrin utama
Log keluar dari rumah dan pulang tanpa log masuk Ubah hala ke skrin log masuk
Pulang tanpa log keluar dari rumah dan log masuk Pergi ke skrin utama