使用 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();
// === 省略 ===
AddAuthentication
AddCookie
您可以通过执行 and 方法启用 cookie 身份验证。
如果不需要更改方案名称,CookieAuthenticationDefaults.AuthenticationScheme
请指定 。
AddAuthorization
options.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");
}
这是按下登录按钮后的身份验证过程。 如果用户名和密码匹配,则可以进行身份验证。
所描述的代码是身份验证所需的最小代码,Claim
ClaimsIdentity
ClaimsPrincipal
并且
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");
}
这是按下登录按钮后的身份验证过程。 如果用户名和密码匹配,则可以进行身份验证。
所描述的代码是身份验证所需的最小代码,Claim
ClaimsIdentity
ClaimsPrincipal
并且
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 身份验证的最低要求实现。 尝试运行它,看看它是如何工作的。 该行为应根据您是否登录而更改。 举个简单的例子,您可以看到以下行为。
操作 | ⇒ | 操作结果 |
---|---|---|
无需登录即可回家 | ⇒ | 重定向至登录屏幕 |
登录 | ⇒ | 转到主屏幕 |
退出家中,无需登录即可回家 | ⇒ | 重定向至登录屏幕 |
无需注销和登录即可回家 | ⇒ | 转到主屏幕 |