原文:ASP.NET Core 登录登出 - 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>

上面这段代码,又多了很多新面孔

  1. 为了判断用户是否已经登录,我们必须使用 User.Identity.IsAuthenticated 来判断,如果返回 true 则说明已经登录,否则表示未登录

  2. 为了显示当前登录的用户名,我们使用了 User.Identity.Name 表达式

  3. 为了在当前视图中使用 User 对象,我们必须引入命名空间 System.Security.Claims

  4. 同时,我们也把 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> &nbsp; <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");
}

这个注销操作也是有很多的名堂可讲了

  1. 因为在 _Layout.cshtml 的登出操作使用的是 HTTP POST 方法。所以这里添加了 [HttpPost] 特性

  2. 注销操作需要与 Identity 框架进行交互,告诉 Identity 框架注销当前用户。这个操作通过调用 _signManager.SignOutAsync 方法来完成,因为这是一个异步方法,所以需要使用 await 来修饰等待

  3. 因为使用了异步方法,所以整个方法也是异步的,那么返回结果也就需要是异步结果,使用 async Task<IActionResult> 来修饰

  4. Identity 注销完成后,当前用户就变成了匿名用户,而匿名用户能访问的只有首页,所以就跳回了首页

保存所有代码,重启应用程序,点击 登出 显示如下

登录功能

  1. 登出成功后,我们可以继续点击 登录 链接来登录,因为还没有实现该功能,所以会出现 404 错误

  2. 从前面可知,登录页面的网址为 /Account/Login,为了实现登录功能,我们需要添加一对动作方法 Login ,一个用于响应 HTTP GET 请求显示登录表单,另一个用户响应 HTTP POST 请求完成登录操作

    [HttpGet]
    public IActionResult Login() {
    return View();
    } [HttpPost]
    public IActionResult Login() {
    return View();
    }
  3. 为了能够记住显示登录之前的页面,我们需要保存来路,这一点,ASP.NET Core MVC 早就已经想好了,只要给我们的方法添加上 returnUrl 即可

    [HttpGet]
    public IActionResult Login(string returnUrl = "" ) {
    return View();
    } [HttpPost]
    public IActionResult Login(string returnUrl = "") {
    return View();
    }
  4. 因为注册的时候只输入了用户名和密码,所以登录的时候也就只有用户名和密码了,所以我们需要创建一个模型,用来接收登录提交的用户数据

    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 而多了一个 RememberMeReturnUrl 用户保存是否记住密码选项和登录来路

    其实这里有多了一个新的特性,就是 Display ,这个特性用于填充登录表单的 <label> 的名字

  5. 接下来我们就可以修改显示表单的 Login 方法将 LoginViewModel 实例传给视图

    [HttpGet]
    public IActionResult Login(string returnUrl = "") {
    var model = new LoginViewModel { ReturnUrl = returnUrl };
    return View(model);
    }
  6. 然后在 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,值为指定的值

  7. 接下来我们实现登录动作,因为 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);
    }
    1. 因为 LoginViewModel 已经包含了 ReturnUrl ,所以我们可以把 returnurl 参数删掉

    2. 检查 ModelState 是否有效。如果它有效,则通过调用 SignInManager 上的 PasswordSignInAsync 来登录用户

      该方法接受四个参数,前三个分别为用户名、密码、和是否记住我三个参数

    3. 如果验证成功且成功登录,则重定向到 returnurl

    4. 如果失败,则显示失败信息给用户

修改完成后,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 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"); if ( string.IsNullOrEmpty(model.ReturnUrl) )
{
model.ReturnUrl = "";
} return View(model);
}
} public class RegisterViewModel
{
[Required, MaxLength(64)]
public string Username { get; set; } [Required, DataType(DataType.Password)]
public string Password { get; set; } [DataType(DataType.Password), Compare(nameof(Password))]
public string ConfirmPassword { get; set; }
} 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; } }
}

重启我们的应用程序,刷新我们的浏览器,访问首页,显示如下

点击 登录 并输入正确的用户名和密码

然后点击 登录 按钮,成功登录后会跳回首页

我们继续点击 登出 ,登出成功后会跳回首页

此时点击 李白 的详情,则会跳转到登录页面,并且 returnurl 会被复制为 /Home/Detail/1

然后点击 登录 按钮,成功登录后会跳转到李白的详情页

ASP.NET Core 登录登出 - ASP.NET Core 基础教程 - 简单教程,简单编程的更多相关文章

  1. flask 实现登录 登出 检查登录状态 的两种方法的总结

    这里我是根据两个项目的实际情况做的总结,方法一(来自项目一)的登录用的是用户名(字符串)和密码,前后端不分离,用form表单传递数据:方法二用的是手机号和密码登录,前后端分离,以json格式传递数据, ...

  2. SpringBoot登录登出切面开发

    阅读本文约“2.5分钟” 本文开发环境是SpringBoot2.X版本. 对于系统而言(这里多指管理系统或部分具备登录登出功能的系统),登录登出是一个类权限验证的过程,现在一般是以token进行校验, ...

  3. Struts2学习第六课 实现登录登出功能

    关于Struts2请求的扩展名问题: 1).org.apache.struts2包下的default.properties中配置了struts2应用的一些常量 2).struts.action.ext ...

  4. ThinkPHP---案例1登录登出和添加部门

    配置文件分3类:系统配置文件,分组配置文件,应用配置文件 ①系统配置文件ThinkPHP/Conf/convention.php: ②分组 / 模块 /平台配置文件Home/Conf/config.p ...

  5. Django项目: 4.用户登录登出功能

    用户登录登出功能 一.功能需求分析 1. 登录退出功能分析 流程图 功能 登录页面 登录功能 退出功能 二.登录页面 1. 接口设计 接口说明 类目 说明 请求方法 GET url定义 /user/l ...

  6. jquery ajax常用的登录登出

    整理jquery+ajax的登录登出方法. //登录 var currentUserId = -1; $(function() { var timestamp = (new Date()).value ...

  7. CAS5.3 单点登录/登出/springboot/springmvc

    环境: jdk:1.8 cas server:5.3.14 + tomcat 8.5 cas client:3.5.1 客户端1:springmvc 传统web项目(使用web.xml) 客户端2:s ...

  8. Android网络开发实例(基于抓包实现的网络模拟登录,登出和强制登出)

    学习Android有几个月了,最近喜欢上了网络编程,于是想通过Android写一些一个小程序用于连接外网.在这里非常感谢雪夜圣诞的支持,非常感谢,给我打开新的一扇门. 1.声明,本程序只能用于西南大学 ...

  9. 利用Vue.js实现登录/登出以及JWT认证

    JSON Web Token 入门教程:http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html 后端代码地址:https ...

随机推荐

  1. 关于.c和.h 和定义变量的问题

    最初调试的时候是因为有个错误在wavplay.h文件中 于是我跳到了recorderl.h中:从图中看到引用了main.h 出现这个问题的具体原因还是不太清楚: 不过我任务是因为: wavplay.h ...

  2. u8和unsigned char的区别

  3. surfingkeys

    https://www.appinn.com/surfingkeys-for-chrome/ 尝试使用.听说能支持js Vimium 不支持拷贝链接的文本. 不支持stop page https:// ...

  4. ios开发runtime学习四:动态添加属性

    #import "ViewController.h" #import "Person.h" #import "NSObject+Property.h& ...

  5. spring mvc 解决csrf跨站请求攻击

    http://www.dewen.net.cn/q/935/spring+mvc+%E8%A7%A3%E5%86%B3csrf%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82% ...

  6. Android的事件分发

    1. Touch事件和绘制事件的异同之处 Touch事件和绘制事件非常相似,都是由ViewRoot派发下来的,可是不同之处在绘制事件是由应用中的某个View发起请求,一层一层上传到ViewRoot.再 ...

  7. jQuery笔记-jQuery筛选器children()详解

    jQuery的选择包含两种,一种是选择器,一种是筛选器.筛选器是对选择器选定的jQuery对象做进一步选择. children()是一个筛选器,顾名思义就是筛选孩子,筛选那些符合条件的孩子. 完整的格 ...

  8. 授人玫瑰 手留余香 --纪念python3.2.3官方文档翻译结束

    当你点击看到这篇文章的时候.你已经得到了祝福. 一个来自夜深人静的码农,在2014年5月19号的01:18分.默默为你献上祝福. 希望你.我和他,每个在IT行业中奋斗的人.能找到属于自己一片天空. 在 ...

  9. [Ramda] Convert a Promise.all Result to an Object with Ramda's zip and zipObj

    In this lesson, we'll use Promise.all to get an array that contains the resolved values from multipl ...

  10. Go 快速入门

    入门 Go 语言需要多久?答案是 -- 读完这篇文章的时间!不妨找一个周末的下午,踏上 Go 之旅吧! 更新记录: 2016.12.12: 完成重制 2016.11.02: 增加重点理解和参考链接 2 ...