ASP.NET Core 用户登录与注销

在本章中,我们将讨论登录和注销功能。与登录相比,注销很容易实现。让我们继续使用 布局视图,因为我们希望构建一个具有一些链接的 UI。这将允许登录用户注销并显示用户名。

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta name = "viewport" content = "width = device-width" />
  5. <title>@ViewBag.Title</title>
  6. </head>
  7. <body>
  8. <div>
  9. @DateTime.Now
  10. </div>
  11. <div>
  12. @RenderBody()
  13. </div>
  14. </body>
  15. </html>
  • 对于匿名用户,我们将显示登录链接。
  • 创建此 UI 所需的所有信息都可以从 Razor 视图上下文中获得。
  • 首先,让我们在布局视图中添加命名空间 System.Security.Claims
  1. @using Microsoft.AspNetCore.Identity
  2. @inject SignInManager<MyCoreWeb.Models.User> SignInManager
  3. @inject UserManager<MyCoreWeb.Models.User> UserManager
  4. <!DOCTYPE html>
  5. <html>
  6. <head>
  7. <meta name="viewport" content="width = device-width" />
  8. <title>@ViewBag.Title</title>
  9. </head>
  10. <body>
  11. <div>
  12. @if (SignInManager.IsSignedIn(User))
  13. {
  14. <div>@User.Identity.Name</div>
  15. <form method="post" asp-controller="Account" asp-action="Logout">
  16. <input type="submit" value="注销" />
  17. </form>
  18. }
  19. else
  20. {
  21. <a asp-controller="Account" asp-action="Login">登录</a>
  22. <a asp-controller="Account" asp-action="Register">注册</a>
  23. }
  24. </div>
  25. <div>
  26. @DateTime.Now
  27. </div>
  28. <div>
  29. @RenderBody()
  30. </div>
  31. </body>
  32. </html>
  • 每个 Razor 视图中都有一个 User 属性,我们希望构建一个显示登录用户名的 UI。这里还提供了扩展方法 IsSignedIn
  • 我们可以调用这个方法,如果它返回 true,我们可以在这里放置一些标记来显示用户名,显示注销按钮。
  • 现在,如果用户已登录,我们可以使用助手方法 GetUserName 显示用户的用户名。
  • 我们必须在表单中创建一个注销按钮,该按钮将 POST 请求到 web 服务器。必须这样做,因为如果您用简单的 GET 请求来允许用户退出,可能回造成一些问题。
  • 我们将强制这个操作为 POST 方式,当用户提交此表单时,我们需要做的就是点击 注销 操作,我们将通过 AccountController 实现该操作,并注销用户。
  • 如果用户未登录,并且我们有一个匿名用户,那么我们需要显示一个链接,该链接将转到 AccountController,特别是 Login 操作,它可以显示文本登录。
  • 我们还需要添加一个链接,让新用户注册并直接转到注册页面。

现在,让我们转到 AccountController 并首先执行注销操作,如以下所示。

  1. [HttpPost]
  2. public async Task<IActionResult> Logout() {
  3. await _signManager.SignOutAsync();
  4. return RedirectToAction("Index", "Home");
  5. }
  • 此操作仅响应 HttpPost。这是一个异步操作。我们将不得不在 Identity 框架上调用另一个异步方法。
  • 我们可以返回 IActionResult 任务,该操作名为 Logout
  • 注销所需要做的就是等待 SignInManagerSignOutAsync 方法。
  • 用户上下文现在已更改;我们现在有一个匿名用户。视图将重定向到主页,我们将返回员工列表。

现在让我们继续构建我们的登录功能。这里,我们需要一对操作,一个响应 HttpGet 请求并显示可用于登录的表单,另一个响应 HTTPPost 请求。

首先,我们需要一个新的 ViewModel 来获取登录数据,因为登录与注册非常不同。因此,让我们添加一个新类并将其称为 LoginViewModel

  1. public class LoginViewModel
  2. {
  3. public string Username { get; set; }
  4. [DataType(DataType.Password)]
  5. public string Password { get; set; }
  6. [Display(Name = "Remember Me")]
  7. public bool RememberMe { get; set; }
  8. public string ReturnUrl { get; set; }
  9. }
  • 当用户登录时,他们必须提供一些信息,如用户名、密码。
  • 第三条信息必须是登录 UI。这些都带有一个小复选框,上面写着 "Remember Me"。这是我们想要会话 cookie 还是想要更持久的 cookie 之间的选择。
  • 为了实现这一功能,我们添加了布尔属性 RememberMe,并使用了显示注释。现在,当我们创建标签时,文本 Remember Me 将显示为带有空格。
  • 作为此 ViewModel 的一部分,我们实际需要的最后一个信息是具有将存储 ReturnUrl 的属性。

现在让我们添加 Login 操作,该操作将响应 Get 请求,如以下所示。

  1. [HttpGet]
  2. public IActionResult Login(string returnUrl = "") {
  3. var model = new LoginViewModel { ReturnUrl = returnUrl };
  4. return View(model);
  5. }
  • 我们将 returnUrl 作为查询字符串中的参数。
  • 可能有时没有 returnUrl。让我们使用空字符串作为默认值。

让我们在 Views → Account 文件夹下创建一个新视图,命名为 Login.chtml。然后添加如下代码:

  1. @model LoginViewModel
  2. @{
  3. ViewBag.Title = "登录";
  4. }
  5. <h2>登录</h2>
  6. <form method="post" asp-controller="Account" asp-action="Login"
  7. asp-route-returnurl="@Model.ReturnUrl">
  8. <div asp-validation-summary="ModelOnly"></div>
  9. <div>
  10. <label>用户名</label>
  11. <input asp-for="Username" />
  12. <span asp-validation-for="Username"></span>
  13. </div>
  14. <div>
  15. <label>密码</label>
  16. <input asp-for="Password" />
  17. <span asp-validation-for="Password"></span>
  18. </div>
  19. <div>
  20. <label>记住我</label>
  21. <input asp-for="RememberMe" />
  22. <span asp-validation-for="RememberMe"></span>
  23. </div>
  24. <input type="submit" value="登录" />
  25. </form>
  • 在这个登录视图中,我们将页面的标题设置为 登录,然后我们有一个表单将发布到 AccountLogin 操作。
  • 我们需要使用一个标签帮助,即 asp-route-returnurl,以确保表单返回的 URL 中有 ReturnUrl
  • 我们需要将该 ReturnUrl 发送回服务器,以便如果用户成功登录,我们可以将其发送到他们试图到达的位置。
  • asp route-idreturnurl 之后添加的任何内容,都将进入请求的某个位置,要么进入 URL 路径,要么作为查询字符串参数。
  • 我们有验证摘要和用户名、密码和 RememberMe 的输入,然后有一个 登录 按钮。

AccountController,执行 Post 操作。这是一个异步方法,因为我们需要调用 Identity 框架并返回一个任务或 IActionResult

  1. [HttpPost]
  2. public async Task<IActionResult> Login(LoginViewModel model)
  3. {
  4. if (ModelState.IsValid)
  5. {
  6. var result = await _signManager.PasswordSignInAsync(model.Username,
  7. model.Password, model.RememberMe, false);
  8. if (result.Succeeded)
  9. {
  10. if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl))
  11. {
  12. return Redirect(model.ReturnUrl);
  13. }
  14. else
  15. {
  16. return RedirectToAction("Index", "Home");
  17. }
  18. }
  19. }
  20. ModelState.AddModelError("", "Invalid login attempt");
  21. return View(model);
  22. }
  • 我们调用 Login,现在我们希望接收到 LoginViewModel
  • 我们需要检查 ModelState 是否有效。如果有效,则通过调用 SignInManager 上的 API 登录用户。
  • PasswordSignInAsync 方法将返回一个结果,如果结果成功,我们知道用户已成功登录。
  • 我们还有一个返回 URL。如果它是有效的本地 Url,我们将被重定向到返回 Url
  • 如果用户刚刚登录,没有设置实体的 ReturnUrl,我们会将用户重定向到 HomeControllerIndex 操作,即首页。
  • 我们可能会遇到用户提供无效密码或无效用户名的情况。我们还需要一个 AddModelError,提示是否存在无效登录尝试。这有助于用户知道是否出现了问题。

现在我们保存并运行代码,如下:

然后点击 登录 按钮,跳转到登录界面:

输入用户名和密码,点击 登录,跳转回首页:

再点击 注销,退出用户登录:

最后可以点击 注册 按钮,进行用户注册。