原文: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. vc弹出USB的方法. 附试验通过的代码!

    vc弹出USB的方法. 附试验通过的代码! http://blog.sina.com.cn/s/blog_4fcd1ea30100qrzn.html (2011-04-15 10:09:48) boo ...

  2. 根据PID获取进程名&根据进程名获取PID

    Liunx中 通过进程名查找进程PID可以通过 pidof [进程名] 来查找.反过来 ,相同通过PID查找进程名则没有相关命令.在linux根目录中,有一个/proc的VFS(虚拟文件系统),系统当 ...

  3. 在Linux上安装及配置MariaDB

    安装MariaDB 1.切换到root用户,首先执行rpm -qa | grep -i mysql检查一下是否有已安装的与MySQL相关的东西,如果有,使用rpm -e --nodeps mysql* ...

  4. 菜单之二:使用xml文件定义菜单 分类: H1_ANDROID 2013-11-03 09:39 1038人阅读 评论(0) 收藏

    参考<疯狂android讲义>2.10节 P174,参见归档project:XmlMenuDemo.zip 一般推荐使用XML文件定义菜单. 基本步骤如下: 1.定义布局文件 为简单显示原 ...

  5. IIS最大并发连接数 = 队列长度 + IIS最大并发工作线程数

    深入理解IIS的多线程工作机制   首先让我们来看看IIS里面的这2个数字:最大并发连接数,队列长度.先说这2个数字在哪里看. 最大并发连接数:在IIS中选中一个网站,右键网站名称,在右键菜单中找到并 ...

  6. GCD网络多线程---同步运行,异步运行,串行队列,并行队列

    总结:同步(无论是串行还是并行)----不又一次开辟子线程 异步(无论是串行还是并行)----开辟子线程 GCD: dispatch queue 主线程的main queue 并行队列 global ...

  7. 微信测试号开发之九 微信网页授权:页面获取用户openid

    原文链接:https://blog.csdn.net/qq_37936542/article/details/78981369 一:配置接口 注意:这里填写的是域名(是一个字符串),而不是URL,因此 ...

  8. mysql的四种启动方式

    查看该版本的相应参数: mysqld --verbose --help     1.mysqld  ./mysqld --defaults-file=/etc/my.cnf --user=mysql ...

  9. js的dom对象(带实例超详细全解)

    js的dom对象(带实例超详细全解) 一.总结 一句话总结: 1.DOM中的方法区分大小写么? 解答:区分 2.DOM中元素和节点的关系式什么? 解答:元素就是标签,节点中有元素节点,也是标签,节点中 ...

  10. 单个和多个checkbox选中事件怎么写

    单个和多个checkbox选中事件怎么写 一.总结 一句话总结: 1.checkbox的事件方法的话主要是change和click 2.checkbox的属性判断的话主要是prop(判断checked ...