ASP.NET Core 用户登录与注销
在本章中,我们将讨论登录和注销功能。与登录相比,注销很容易实现。让我们继续使用 布局视图,因为我们希望构建一个具有一些链接的 UI。这将允许登录用户注销并显示用户名。
<!DOCTYPE html><html><head><meta name = "viewport" content = "width = device-width" /><title>@ViewBag.Title</title></head><body><div>@DateTime.Now</div><div>@RenderBody()</div></body></html>
- 对于匿名用户,我们将显示登录链接。
- 创建此 UI 所需的所有信息都可以从 Razor 视图上下文中获得。
- 首先,让我们在布局视图中添加命名空间 System.Security.Claims。
@using Microsoft.AspNetCore.Identity@inject SignInManager<MyCoreWeb.Models.User> SignInManager@inject UserManager<MyCoreWeb.Models.User> UserManager<!DOCTYPE html><html><head><meta name="viewport" content="width = device-width" /><title>@ViewBag.Title</title></head><body><div>@if (SignInManager.IsSignedIn(User)){<div>@User.Identity.Name</div><form method="post" asp-controller="Account" asp-action="Logout"><input type="submit" value="注销" /></form>}else{<a asp-controller="Account" asp-action="Login">登录</a><a asp-controller="Account" asp-action="Register">注册</a>}</div><div>@DateTime.Now</div><div>@RenderBody()</div></body></html>
- 每个 Razor 视图中都有一个
User属性,我们希望构建一个显示登录用户名的 UI。这里还提供了扩展方法IsSignedIn。 - 我们可以调用这个方法,如果它返回 true,我们可以在这里放置一些标记来显示用户名,显示注销按钮。
- 现在,如果用户已登录,我们可以使用助手方法
GetUserName显示用户的用户名。 - 我们必须在表单中创建一个注销按钮,该按钮将 POST 请求到 web 服务器。必须这样做,因为如果您用简单的 GET 请求来允许用户退出,可能回造成一些问题。
- 我们将强制这个操作为 POST 方式,当用户提交此表单时,我们需要做的就是点击 注销 操作,我们将通过
AccountController实现该操作,并注销用户。 - 如果用户未登录,并且我们有一个匿名用户,那么我们需要显示一个链接,该链接将转到 AccountController,特别是
Login操作,它可以显示文本登录。 - 我们还需要添加一个链接,让新用户注册并直接转到注册页面。
现在,让我们转到 AccountController 并首先执行注销操作,如以下所示。
[HttpPost]public async Task<IActionResult> Logout() {await _signManager.SignOutAsync();return RedirectToAction("Index", "Home");}
- 此操作仅响应 HttpPost。这是一个异步操作。我们将不得不在 Identity 框架上调用另一个异步方法。
- 我们可以返回 IActionResult 任务,该操作名为
Logout。 - 注销所需要做的就是等待 SignInManager 的
SignOutAsync方法。 - 用户上下文现在已更改;我们现在有一个匿名用户。视图将重定向到主页,我们将返回员工列表。
现在让我们继续构建我们的登录功能。这里,我们需要一对操作,一个响应 HttpGet 请求并显示可用于登录的表单,另一个响应 HTTPPost 请求。
首先,我们需要一个新的 ViewModel 来获取登录数据,因为登录与注册非常不同。因此,让我们添加一个新类并将其称为 LoginViewModel。
public class LoginViewModel{public string Username { get; set; }[DataType(DataType.Password)]public string Password { get; set; }[Display(Name = "Remember Me")]public bool RememberMe { get; set; }public string ReturnUrl { get; set; }}
- 当用户登录时,他们必须提供一些信息,如用户名、密码。
- 第三条信息必须是登录 UI。这些都带有一个小复选框,上面写着 "Remember Me"。这是我们想要会话 cookie 还是想要更持久的 cookie 之间的选择。
- 为了实现这一功能,我们添加了布尔属性
RememberMe,并使用了显示注释。现在,当我们创建标签时,文本 Remember Me 将显示为带有空格。 - 作为此 ViewModel 的一部分,我们实际需要的最后一个信息是具有将存储
ReturnUrl的属性。
现在让我们添加 Login 操作,该操作将响应 Get 请求,如以下所示。
[HttpGet]public IActionResult Login(string returnUrl = "") {var model = new LoginViewModel { ReturnUrl = returnUrl };return View(model);}
- 我们将
returnUrl作为查询字符串中的参数。 - 可能有时没有
returnUrl。让我们使用空字符串作为默认值。
让我们在 Views → Account 文件夹下创建一个新视图,命名为 Login.chtml。然后添加如下代码:
@model LoginViewModel@{ViewBag.Title = "登录";}<h2>登录</h2><form method="post" asp-controller="Account" asp-action="Login"asp-route-returnurl="@Model.ReturnUrl"><div asp-validation-summary="ModelOnly"></div><div><label>用户名</label><input asp-for="Username" /><span asp-validation-for="Username"></span></div><div><label>密码</label><input asp-for="Password" /><span asp-validation-for="Password"></span></div><div><label>记住我</label><input asp-for="RememberMe" /><span asp-validation-for="RememberMe"></span></div><input type="submit" value="登录" /></form>
- 在这个登录视图中,我们将页面的标题设置为 登录,然后我们有一个表单将发布到
AccountLogin操作。 - 我们需要使用一个标签帮助,即
asp-route-returnurl,以确保表单返回的 URL 中有 ReturnUrl。 - 我们需要将该 ReturnUrl 发送回服务器,以便如果用户成功登录,我们可以将其发送到他们试图到达的位置。
- 在 asp route-、id 或 returnurl 之后添加的任何内容,都将进入请求的某个位置,要么进入 URL 路径,要么作为查询字符串参数。
- 我们有验证摘要和用户名、密码和 RememberMe 的输入,然后有一个 登录 按钮。
AccountController,执行 Post 操作。这是一个异步方法,因为我们需要调用 Identity 框架并返回一个任务或 IActionResult。
[HttpPost]public async Task<IActionResult> Login(LoginViewModel model){if (ModelState.IsValid){var result = await _signManager.PasswordSignInAsync(model.Username,model.Password, model.RememberMe, false);if (result.Succeeded){if (!string.IsNullOrEmpty(model.ReturnUrl) && Url.IsLocalUrl(model.ReturnUrl)){return Redirect(model.ReturnUrl);}else{return RedirectToAction("Index", "Home");}}}ModelState.AddModelError("", "Invalid login attempt");return View(model);}
- 我们调用
Login,现在我们希望接收到 LoginViewModel。 - 我们需要检查
ModelState是否有效。如果有效,则通过调用 SignInManager 上的 API 登录用户。 PasswordSignInAsync方法将返回一个结果,如果结果成功,我们知道用户已成功登录。- 我们还有一个返回 URL。如果它是有效的本地 Url,我们将被重定向到返回 Url。
- 如果用户刚刚登录,没有设置实体的 ReturnUrl,我们会将用户重定向到 HomeController 的
Index操作,即首页。 - 我们可能会遇到用户提供无效密码或无效用户名的情况。我们还需要一个
AddModelError,提示是否存在无效登录尝试。这有助于用户知道是否出现了问题。
现在我们保存并运行代码,如下:

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

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

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

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