- ASP.NET Core 教程
- ASP.NET Core - 首页
- ASP.NET Core - 概述
- ASP.NET Core - 环境设置
- ASP.NET Core - 新建项目
- ASP.NET Core - 项目布局
- ASP.NET Core - project.json
- ASP.NET Core - 配置
- ASP.NET Core - 中间件
- ASP.NET Core - 异常处理
- ASP.NET Core - 静态文件
- ASP.NET Core - 设置 MVC
- ASP.NET Core - MVC 设计模式
- ASP.NET Core - 路由
- ASP.NET Core - 属性路由
- ASP.NET Core - 操作结果
- ASP.NET Core - 视图
- 设置 Entity Framework
- ASP.NET Core - DbContext
- ASP.NET Core - Razor 布局视图
- ASP.NET Core - Razor 视图启动
- ASP.NET Core - Razor 视图导入
- ASP.NET Core - Razor 标签助手
- ASP.NET Core - Razor 编辑表单
- ASP.NET Core - Identity 概述
- ASP.NET Core - Authorize 属性
- Identity 配置
- ASP.NET Core - Identity 迁移
- ASP.NET Core - 用户注册
- ASP.NET Core - 创建用户
- ASP.NET Core - 登录和注销
- ASP.NET Core 有用资源
- ASP.NET Core - 快速指南
- ASP.NET Core - 有用资源
- ASP.NET Core - 讨论
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 System.Security.Claims <!DOCTYPE html> <html> <head> <meta name = "viewport" content = "width = device-width" /> <title>@ViewBag.Title</title> </head> <body> <div> @if (User.IsSignedIn()) { <div>@User.GetUserName()</div> <form method = "post" asp-controller = "Account" aspcontroller = "Logout"> <input type = "submit" value = "Logout"/> </form> } else { <a asp-controller = "Account" asp-action = "Login">Login</a> <a asp-controller = "Account" asp-action = "Register">Register</a> } </div> <div> @DateTime.Now </div> <div> @RenderBody() </div> </body> </html>
在每个Razor视图中都可使用一个`User`属性,我们想要构建一个显示已登录用户名 的UI。这里还提供了一个扩展方法`IsSignedIn`。
我们可以调用此方法,如果它返回true,则可以在此处放置一些标记来显示用户名,显示注销按钮。
现在,如果用户已登录,我们可以使用辅助方法`GetUserName`显示用户的用户名。
我们必须在一个表单内构建注销按钮,该表单将发布到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's 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。这些带有一个小复选框,上面写着——“您是否要记住我”。这是我们是否想要会话cookie或更永久的cookie的选择。
为了允许此功能,我们添加了一个布尔属性`RememberMe`,并使用了`Display`特性。现在,当我们构建标签时,文本“记住我”将带空格显示。
我们实际上希望作为此ViewModel一部分的最后一条信息是拥有一个将存储`ReturnUrl`的属性。
现在让我们添加将响应Get请求的`Login`操作,如下面的程序所示。
[HttpGet] public IActionResult Login(string returnUrl = "") { var model = new LoginViewModel { ReturnUrl = returnUrl }; return View(model); }
我们将`returnUrl`作为查询字符串中的参数。
`returnUrl`可能并不总是存在。让我们使用空字符串作为默认值。
我们现在将通过在`Views → Account`文件夹中添加新的MVC视图页面来添加一个新视图。
在中间窗格中,选择MVC视图页面并将其命名为`Login.cshtml`,然后单击“添加”按钮。让我们在`Login.cshtml`文件中添加以下代码。
@model LoginViewModel @{ ViewBag.Title = "Login"; } <h2>Login</h2> <form method = "post" asp-controller = "Account" asp-action = "Login" asp-route-returnurl = "@Model.ReturnUrl"> <div asp-validation-summary = "ValidationSummary.ModelOnly"></div> <div> <label asp-for = "Username"></label> <input asp-for = "Username" /> <span asp-validation-for = "Username"></span> </div> <div> <label asp-for = "Password"></label> <input asp-for = "Password" /> <span asp-validation-for = "Password"></span> </div> <div> <label asp-for = "RememberMe"></label> <input asp-for = "RememberMe" /> <span asp-validation-for = "RememberMe"></span> </div> <input type = "submit" value = "Login" /> </form>
在此登录视图中,我们将页面的标题设置为“登录”,然后我们有一个表单,它将发布到`AccountLogin`操作。
我们需要使用标签助手`asp-route-returnurl`来确保`ReturnUrl`存在于表单发布到的URL中。
我们需要将`ReturnUrl`发送回服务器,以便如果用户成功登录,我们可以将其发送到他们试图到达的地方。
在`asp-route-`之后添加的任何内容,例如id或returnurl,无论您在那里有什么,都将进入请求中的某个位置,要么进入URL路径,要么作为查询字符串参数。
我们有`ValidationSummary`和用户名、密码和`RememberMe`的输入,然后我们有一个提交按钮。
`AccountController`,并实现Post操作。此操作响应HttpPost。这将是一个异步方法,因为我们需要调用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`方法将返回一个结果,如果结果成功,我们就知道用户已成功登录。
我们还有一个`returnUrl`。如果它是一个有效的本地Url,我们将被重定向到`returnUrl`。
如果用户刚刚登录并且没有任何特定的地方要去,我们将把用户重定向到`HomeController`的`Index`操作。
我们可能遇到用户提供无效密码或无效用户名的情况。我们还需要添加一个模型错误,提示是否存在无效的登录尝试。这有助于用户知道是否出现错误。
现在让我们保存所有内容并运行应用程序。
我们现在有“登录”和“注册”链接。让我们点击“登录”链接。
让我们使用我们在上一章中创建的用户登录,指定`用户名`和`密码`,并选中`记住我`复选框。
单击“登录”按钮时,浏览器会询问您是否要保存localhost的密码。让我们单击“是”按钮。
现在,让我们通过单击“注销”按钮注销。
作为匿名用户,让我们尝试编辑员工详细信息。
您现在可以看到我们已被重定向到`Login`视图。
让我们使用您的用户名和密码登录,并选中“记住我”复选框。
现在,单击“登录”按钮。
您现在可以看到我们被定向到我们想要编辑的URL。这是因为我们适当地处理了`returnUrl`。