2018-09-29 1338
原文:ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程
上一章节我们总算完善了注册的功能,而且也添加了一个用户,现在,我们是时候继续完善登录登出功能了。
本章节应该是我们这个序列的最后一章节,因为本章节接收,我们大概的基础的 ASP.NET Core 知识都讲解了一遍。虽然很肤浅,但总算日常使用的知识点都有所涉及
本章节,我们将学习登录和注销功能。 与登录相比,注销相当简单。
我们首先重新布局下视图,在首页中去掉登录和注册链接,而改成在 _Layout.cshtml
布局视图中添加这些链接,如果用户已经登录,则隐藏登录和注册,而显示用户名和注销按钮
我们首先来看看之前的 _Layout.cshtml
模板的内容
<!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>
删除 @DateTime.Now
然后改成如下内容
@using System.Security.Claims
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<title>@ViewBag.Title</title>
<style>
body {
margin: 10px auto;
text-align: center
}
table {
margin: 0 auto;
width: 90%
}
table, th, td {
border: 1px solid #eee;
border-collapse: collapse;
border-spacing: 0;
padding: 5px;
text-align: center
}
.txt-left {
text-align: left;
}
</style>
</head>
<body>
<div>
@if ( User.Identity.IsAuthenticated ) {
<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="Signup">注册</a>
}
</div>
<div>@RenderBody()</div>
</body>
</html>
上面这段代码,又多了很多新面孔
为了判断用户是否已经登录,我们必须使用 User.Identity.IsAuthenticated
来判断,如果返回 true
则说明已经登录,否则表示未登录
为了显示当前登录的用户名,我们使用了 User.Identity.Name
表达式
为了在当前视图中使用 User
对象,我们必须引入命名空间 System.Security.Claims
同时,我们也把 Index.cshtml
中的样式移到了当前视图
接着修改 Index.cshtml
,去掉注册链接,去掉样式,修改完成后代码显示如下
@model HomePageViewModel
@{
ViewBag.Title = "职工列表";
}
<h1>职工列表</h1>
<table>
<tr>
<td>ID</td>
<td>姓名</td>
<td class="txt-left">操作</td>
</tr>
@foreach (var employee in Model.Employees)
{
<tr>
<td>@employee.ID</td>
<td>@employee.Name</td>
<td class="txt-left"><a asp-action="Detail" asp-route-Id="@employee.ID">详情</a> <a asp-controller="Home" asp-action="Edit"
asp-route-id="@employee.ID">编辑</a></td>
</tr>
}
</table>
<br/>
<p><a asp-action="Create" >新增员工</a></p>
保存所有代码,刷新浏览器,访问首页,显示如下
因为之前注册的时候我们已经登录过了,所以这时候就会显示登录状态
如果点击 登出 则会报错,原因是我们还没实现该功能呢
修改 AccountController
类添加动作方法 Logout
[HttpPost]
public async Task<IActionResult> Logout() {
await _signManager.SignOutAsync();
return RedirectToAction("Index", "Home");
}
这个注销操作也是有很多的名堂可讲了
因为在 _Layout.cshtml
的登出操作使用的是 HTTP POST 方法。所以这里添加了 [HttpPost]
特性
注销操作需要与 Identity
框架进行交互,告诉 Identity
框架注销当前用户。这个操作通过调用 _signManager.SignOutAsync
方法来完成,因为这是一个异步方法,所以需要使用 await
来修饰等待
因为使用了异步方法,所以整个方法也是异步的,那么返回结果也就需要是异步结果,使用 async Task<IActionResult>
来修饰
当 Identity
注销完成后,当前用户就变成了匿名用户,而匿名用户能访问的只有首页,所以就跳回了首页
保存所有代码,重启应用程序,点击 登出 显示如下
登出成功后,我们可以继续点击 登录 链接来登录,因为还没有实现该功能,所以会出现 404 错误
从前面可知,登录页面的网址为 /Account/Login
,为了实现登录功能,我们需要添加一对动作方法 Login
,一个用于响应 HTTP GET 请求显示登录表单,另一个用户响应 HTTP POST 请求完成登录操作
[HttpGet] public IActionResult Login() { return View(); } [HttpPost] public IActionResult Login() { return View(); }
为了能够记住显示登录之前的页面,我们需要保存来路,这一点,ASP.NET Core MVC 早就已经想好了,只要给我们的方法添加上 returnUrl
即可
[HttpGet] public IActionResult Login(string returnUrl = "" ) { return View(); } [HttpPost] public IActionResult Login(string returnUrl = "") { return View(); }
因为注册的时候只输入了用户名和密码,所以登录的时候也就只有用户名和密码了,所以我们需要创建一个模型,用来接收登录提交的用户数据
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; } }
与 RegisterViewModel
不同的是,少了 ConfirmPassword
而多了一个 RememberMe
和 ReturnUrl
用户保存是否记住密码选项和登录来路
其实这里有多了一个新的特性,就是 Display
,这个特性用于填充登录表单的 <label>
的名字
接下来我们就可以修改显示表单的 Login
方法将 LoginViewModel
实例传给视图
[HttpGet] public IActionResult Login(string returnUrl = "") { var model = new LoginViewModel { ReturnUrl = returnUrl }; return View(model); }
然后在 Views/Account
目录下新建一个 Login.cshtml
登录表单视图,内容如下
@model LoginViewModel
@{
ViewBag.Title = "Login";
}
<h2>登录</h2>
<form method="post" asp-controller="Account" asp-action="Login"
asp-route-returnurl = "@Model.ReturnUrl">
<div asp-validation-summary="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="登录" />
</form>
代码很普通,很多知识我们之前已经接触过了,不过唯一的不同的就是 asp-route-returnurl = "@Model.ReturnUrl"
asp-route-returnurl
属性会把当前值附在提交的 URL 后面,键名为 returnurl
,值为指定的值
接下来我们实现登录动作,因为 LoginViewModel
已经包含了 ReturnUrl
,所以我们可以把 returnurl
参数删掉
[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"); } } } if ( string.IsNullOrEmpty(model.ReturnUrl) ) { model.ReturnUrl = ""; } ModelState.AddModelError("","Invalid login attempt"); return View(model); }
因为 LoginViewModel
已经包含了 ReturnUrl
,所以我们可以把 returnurl
参数删掉
检查 ModelState
是否有效。如果它有效,则通过调用 SignInManager
上的 PasswordSignInAsync
来登录用户
该方法接受四个参数,前三个分别为用户名、密码、和是否记住我三个参数
如果验证成功且成功登录,则重定向到 returnurl
如果失败,则显示失败信息给用户
修改完成后,AccountController.cs
的完整代码如下
using System;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using System.ComponentModel.DataAnnotations;
using HelloWorld.Models;
namespace HelloWorld.Controllers
{
public class AccountController : Controller
{
private SignInManager<User> _signManager;
private UserManager<User> _userManager;
public AccountController(UserManager<User> userManager, SignInManager<User> signManager)
{
_userManager = userManager;
_signManager = signManager;
}
[HttpGet]
public ViewResult Signup()
{
return View();
}
[HttpPost]
public async Task<IActionResult> Signup(RegisterViewModel model)
{
if (ModelState.IsValid)
{
var user = new User { UserName = model.Username };
var result = await _userManager.CreateAsync(user, model.Password);
if (result.Succeeded)
{
await _signManager.SignInAsync(user, false);
return RedirectToAction("Index", "Home");
}
else
{
foreach (var error in result.Errors)
{
ModelState.AddModelError("", error.Description);
}
}
}
return View();
}
[HttpPost]
public async Task<IActionResult> Logout() {
await _signManager.SignOutAsync();
return RedirectToAction("Index", "Home");
}
[HttpGet]
public IActionResult Login(string returnUrl = "")
{
var model = new LoginViewModel { ReturnUrl = returnUrl };
return View(model);
}
[HttpPost]
public async Task<IActionResult> Login(LoginViewModel model)
{
if (ModelState.IsValid)
{
var