快速掌握 ASP.NET 身份认证框架 Identity - 登录与登出

推荐关注「码侠江湖」星标,时刻不忘江湖事

这是 ASP.NET Core Identity 系列的第三篇文章,上一篇文章讲解了如何在 ASP.NET Core Identity 中实现用户注册。

那么,这篇文章讲一讲如何在 ASP.NET Core Identity 中实现用户的登录与登出。

点击上方或后方蓝字,阅读 ASP.NET Core Identity 系列合集。

本篇文章的示例项目:https://github.com/zilor-net/IdentitySample/tree/main/Sample03

d85c6383df162cea86747ec950bcecf4.png

身份认证

说到用户登录,就很容易的想到身份认证,这是确认用户身份的过程。

这个过程通过一系列操作,根据数据库中用户留存的凭证,去验证用户提交的凭证,这里的凭证一般就是账号和密码。

为了使用户能够提供凭证,应用程序就需要一个登陆页面,通过提供登录表单,与用户进行交互。

为了实现登陆操作,我们要做的第一件事,就是禁止未经认证的用户,访问 Home 控制器中的 Employees 操作方法。

为此,我们必须为这个操作,添加[Authorize]特性:

[Authorize]
public async Task<IActionResult> Employees()

然后,在启动类中注册身份认证与授权中间件:

// 认证
app.UseAuthentication();
// 授权
app.UseAuthorization();

需要注意的是,由于管道中间件有执行顺序,所以身份认证中间件,必须在授权中间件之前注册。

如果此时运行应用程序,并单击 Employees 链接,我们会看到一个 404 Not Found 的响应。

cd5aa8ef1670b16ef362b025108522eb.png

之所以会出现这种情况,是因为默认情况下,ASP.NET Core Identity 会尝试将未认证的用户,重定向到 /Account/Login 以引导用户登录,然而这个路由对应的操作我们并没有提供。

另外,我们还可以在地址栏中看到一个 ReturnUrl 的查询参数,它提供了用户重定向到登录页面之前的操作路径。

也就是说,当用户登陆成功后,会重定向回登陆之前的页面。

登录

现在,让我们创建与登录相关的东西。

首先是用户登录模型,用来接受登录表单中用户提交的登录凭证。

「Models」 文件夹中,创建一个 「UserLoginModel」 类:

public class UserLoginModel
{[Display(Name = "电子邮箱")][Required(ErrorMessage = "电子邮箱不能为空")][EmailAddress(ErrorMessage = "电子邮箱格式不正确")]public string Email { get; set; }[Display(Name = "密码")][Required(ErrorMessage = "密码不能为空")][DataType(DataType.Password)]public string Password { get; set; }[Display(Name = "记住账号")]public bool RememberMe { get; set; }
}

接下来,在 「AccountController」 控制器中,创建 「Login」 操作方法:

[HttpGet]
public IActionResult Login()
{return View();
}[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(UserLoginModel userModel)
{return View();
}

以及包含登录表单的 「Login」 视图:

746afaab4b95bb25833c981ffc159342.png

现在这个登录视图,只有通过访问受保护的操作,才能够被访问。

但这不符合常理,我们必须提供一个单独的登录链接,让我们修改 _LoginPartial 分部视图:

<ul class="navbar-nav"><li class="nav-item"><a class="nav-link text-dark" asp-controller="Account"asp-action="Login">登录</a></li><li class="nav-item"><a class="nav-link text-dark" asp-controller="Account"asp-action="Register">注册</a></li></ul>

启动应用,可以重复上一次的操作,验证登录页面是否会被打开:

d1aa6d96225ec8a5c3adbe686322ae17.png

当我们点击登录按钮时,表单数据会被提交到 POST 请求的 「Login」 操作,但是现在还没有任何登录逻辑。

接下来,让我们修改 Login 方法,实现登录逻辑:

using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(UserLoginModel userModel)
{if(!ModelState.IsValid){return View(userModel);}var user = await _userManager.FindByEmailAsync(userModel.Email);if(user != null && await _userManager.CheckPasswordAsync(user, userModel.Password)){var identity = new ClaimsIdentity(IdentityConstants.ApplicationScheme);identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));identity.AddClaim(new Claim(ClaimTypes.Name, user.UserName));// 可以添加更多自定义用户信息identity.AddClaim(new Claim("firstname", user.FirstName));identity.AddClaim(new Claim("lastname", user.LastName));var roles = await _userManager.GetRolesAsync(user);foreach (var role in roles){identity.AddClaim(new Claim(ClaimTypes.Role, role));}await HttpContext.SignInAsync(IdentityConstants.ApplicationScheme,new ClaimsPrincipal(identity));return RedirectToAction(nameof(HomeController.Index), "Home");}else{ModelState.AddModelError("", "无效的用户名或密码");return View();}
}

解释一下这段代码,验证模型是否无效,如果无效,就直接返回视图。

之后,使用 「UserManager」 中的 「FindByEmailAsync」 方法,通过电子邮件查询用户。

使用 「CheckPasswordAsync」 方法,检查用户密码是否与数据库中的哈希密码匹配。

如果用户存在,并且密码验证通过,就创建一个 「ClaimsIdentity」 对象。

ClaimsIdentity 代表身份对象,其中包含两个声明:ID和用户名。

当然,你也可以添加更多的自定义用户信息,比如姓名、角色等。

ApplicationScheme 表示身份方案的名称,这是一个预定义好的静态变量,最终体现为一个名称为 「Identity.Application」 的 Cookie。

之后,通过 「SignInAsync」 方法进行登录,第一个参数就是刚才的方案名称,第二个参数是一个身份持有对象,也就是真正代表用户的对象,我们需要给它提供一个身份。

这个方法会在我们的浏览器中,创建名为 「Identity.Application」 的 Cookie 数据,其值就是身份对象中的信息。

登陆成功后,会将用户重定向到之前的 「Index」 页面。

如果数据库中不存在该用户,或密码不匹配,那就返回一个带有错误消息的视图。

接着,修改一下 「Employees」 视图,让我们登录后可以看到身份信息:

<h2>Claim details</h2>
<ul>@foreach (var claim in User.Claims){<li><strong>@claim.Type</strong>: @claim.Value</li>}
</ul>

现在启动应用,点击 「Employees」 连接,由于我们现在通过认证,所以会跳转到登录页面。

使用刚才注册的用户登录,然后再次点击 「Employees」 连接,可以看到 「Employees」 的数据表格,以及下方的身份信息了。

f05e69f5aa4d05ef87b12f7cac154a75.png

我们还可以查看浏览器里的 Cookies ,可以看到有两个 Cookie:

f4e88dcf29ab9236e59cce54165889e1.png

「.AspNetCore.Identity.Application」 中保存了身份信息;

「.AspNetCore.Antiforgery.xxxxx」 中保存了验证表单的令牌。

跳转源地址

不过现在还有个小问题,前面我说过,如果用户未经授权访问受保护的操作,就会将被重定向到 Login 页面。

此时,URL 中会包含一个 「ReturnUrl」 查询参数,该参数显示用户来自的源页面。

但在我们的示例中,我们直接将用户导航到了 「/Home/Index」,而不是跳转到 ReturnUrl 中的源页面。

想要实现这个功能,我们需要修改 Get 请求的 「Login」 操作:

[HttpGet]
public IActionResult Login(string returnUrl = null)
{ViewData["ReturnUrl"] = returnUrl;return View();
}

然后,修改 「Login.cshtml」 视图文件:

<form asp-action="Login" asp-route-returnUrl="@ViewData["ReturnUrl"]">

通过 ViewData 将 「returnUrl」 的值,传到视图的表单中的路由参数。

当提交表单时,「ReturnUrl」 就会通过路由参数,提交给 POST 请求的 「Login」 操作。

所以,我们还需要修改 POST 请求的 「Login」 操作:

public async Task<IActionResult> Login(UserLoginModel userModel, string returnUrl = null)

先添加一个 returnUrl 参数,然后创建一个用来重定向的普通方法:

private IActionResult RedirectToLocal(string returnUrl)
{if (Url.IsLocalUrl(returnUrl))return Redirect(returnUrl);elsereturn RedirectToAction(nameof(HomeController.Index), "Home");}

这个方法会先检查 「returnUrl」 是不是本地 URL,如果是,就将用户重定向到该地址,否则,就将用户重定向到主页。

最后,修改 「Login」 操作的返回值,调用刚才添加的方法:

return RedirectToLocal(returnUrl);

启动应用,可以看到,现在已经可以正确跳转到源地址了。

需要说明的是,我们的登录操作位于 「/Account/Login」 路由地址,这是 ASP.NET Core Identity 的默认登录路由地址。

如果你不想使用默认的地址,可以在服务配置方法中进行配置,比如:

builder.Services.ConfigureApplicationCookie(o => o.LoginPath = "/Authentication/Login");

简化登录

前面我们演示的身份认证是完全版的。但是,如果你不需要完全控制身份认证的逻辑,那么有一个更简单的方法:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login([FromServices]SignInManager<User> signInManager, UserLoginModel userModel, string returnUrl = null)
{if(!ModelState.IsValid){return View(userModel);}var result = await signInManager.PasswordSignInAsync(userModel.Email, userModel.Password, userModel.RememberMe, false);if (result.Succeeded){return RedirectToLocal(returnUrl);}ModelState.AddModelError("", "无效的用户名或密码");return View();
}

在登录操作中注入 「SignInManager」 服务,使用 「PasswordSignInAsync」 方法,代替之前的验证逻辑。

这个方法接受四个参数:用户名、密码、持久化标志和登录锁定标志。

关于登录锁定功能后面我们再详说,这里先把它设置为false。

这个方法,完成了我们在前面演示的所有登录逻辑。

此外,它还返回具有四个属性值的结果,其中 「Succeeded」 属性代表是否成功。

登出

使用 ASP.NET Core Identity 实现登录就是如此简单,登出就更简单了。

首先,修改 「_LoginPartial」 登录分部视图:

@using Microsoft.AspNetCore.Identity
@using IdentitySample.Entites
@inject SignInManager<User> _signInManager@{var lastname = User.Claims.SingleOrDefault(claim => claim.Type == "lastname")?.Value;
}<ul class="navbar-nav">@if (_signInManager.IsSignedIn(User)){<li class="nav-item"><a class="nav-link text-dark" asp-controller="Home" asp-action="Index" title="Welcome">欢迎 @lastname!</a></li><li class="nav-item"><a class="nav-link text-dark" asp-controller="Account"asp-action="Logout">登出</a></li>}else{<li class="nav-item"><a class="nav-link text-dark" asp-controller="Account"asp-action="Login">登录</a></li><li class="nav-item"><a class="nav-link text-dark" asp-controller="Account"asp-action="Register">注册</a></li>}
</ul>

在视图中注入 「SignInManager」 服务,使用它来判断用户是否登录,然后渲染不同的片段。

已登录就显示欢迎语与登出按钮,未登录和之前一样。

接下来,实现 「Logout」 登出操作:

public async Task<IActionResult> Logout([FromServices]SignInManager<User> signInManager)
{await signInManager.SignOutAsync();return RedirectToAction(nameof(HomeController.Index), "Home");
}

「SignOutAsync」 方法会通过删除 Cookies,来实现用户的登出。

小结

现在,我们已经实现了用户的登录与登出,具体的代码可以参看示例项目,下篇文章将会讲解用户在忘记密码后,如何通过邮件服务重置密码。

更多精彩内容,请关注我▼▼

6d99193a86099230bf308185890cfc6b.gif

如果喜欢我的文章,那么

在看和转发是对我最大的支持!

(戳下面蓝字阅读)

3687d97a63f879692c217142a70d83a2.png

推荐关注微信公众号:码侠江湖

                        0f5dfebba62b767260503c7eb81a296c.png觉得不错,点个在看再走哟

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/285800.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言试题七十九之请编写函数实现自然底数 e=2.718281828

📃个人主页:个人主页 🔥系列专栏:C语言试题200例目录 💬推荐一款刷算法、笔试、面经、拿大公司offer神器 👉 点击跳转进入网站 ✅作者简介:大家好,我是码莎拉蒂,CSDN博客专家(全站排名Top 50),阿里云博客专家、51CTO博客专家、华为云享专家 1、题目 请编写函数…

如何创建NFT并OpenSea上展示《alchemy How to Develop an NFT Smart Contract》译

翻译&#xff1a;1_bit 原文&#xff1a;https://docs.alchemy.com/docs/how-to-develop-an-nft-smart-contract-erc721-with-alchemy 注&#xff1a;英文不好各位多多担待 1.如何使用 Alchemy 开发一个 NFT 智能合约 在你第一次使用 solidity 时开发一个智能合约部署在区块…

Jmeter之Bean shell使用(二)

上一篇Jmeter之Bean shell使用(一)简单介绍了下Jmeter中的Bean shell&#xff0c;本文是对上文的一个补充&#xff0c;主要总结下常用的几种场景和方法&#xff0c;相信这些基本可以涵盖大部分的需求。本节内容如下&#xff1a; 一、操作变量 二、操作属性 三、自定义函数 四、…

【ArcGIS风暴】ArcGIS中国地表覆盖数据GlobeLand30预处理(批量投影、拼接、掩膜提取)附成品下载

结果示意图&#xff1a; GlobeLand30是30米空间分辨率全球地表覆盖数据&#xff0c;目前可供下载的有3年的数据&#xff1a;2000-2010-2020。本文主要讲解在ArcGIS10.6平台下进行GlobeLand30的预处理操作&#xff0c;主要预处理步骤包括&#xff1a;批量分幅投影转换、批量分幅…

Asp.Net MVC4入门指南(9):查询详细信息和删除记录

在本教程中&#xff0c;您将查看自动生成的Details和Delete方法。 查询详细信息和删除记录 打开Movie控制器并查看Details方法。 ?Code First 使得您可以轻松的使用Find方法来搜索数据。一个重要的安全功能内置到了方法中。方法首先验证Find方法已经找到了一部电影&#xff0c…

漫画:什么是架构师?

于是&#xff0c;小灰去向大黄请教 这是有关未来的故事&#xff1a; 从前&#xff0c;有一个赶路的人路过一片工地&#xff0c;看到三个年轻人在工地上搬砖。 于是&#xff0c;他问其中一个人&#xff1a; 于是&#xff0c;他又问了第二个人&#xff1a; 于是&#xff0c;他又问…

Andoroid之BottomNavigationView右上角添加红点布局和自动跳转到底部具体第几个Tab

一、需求 BottomNavigationView右上角添加红点布局和自动跳转到底部具体第几个Tab 我们知道BottomNavigationView是在google material里面的组件 github地址如下: https://github.com/material-components/material-components-android 二、效果图片爆照 三、BottomNaviga…

FileSystemWatcher监听文件是否有被修改

作用&#xff1a;监听文件系统更改通知&#xff0c;并在目录或目录中的文件更改时引发事件。 需求&#xff1a;监听特定文件是否修改&#xff0c;然后做出相应的操作。 方法&#xff1a; ①利用一个线程&#xff0c;一直去查找该指定的文件是否有被修改&#xff0c;如果修改则操…

一、FFmpeg 的初尝试《FFmpeg 音视频开发基础入门到实战》

学习目标 了解 FFmpeg学习 FFmpeg 工具的下载及环境配置了解 FFmpeg 工具的使用方式了解 FFmpeg play 的使用方法了解 FFmpeg paly 的音量设置、窗口设置、音量设置等设置方法 一、了解 FFmpeg FFmpeg 是一个音视频处理的工具&#xff0c;通过 FFmpeg 可以对视频进行旋转、缩…

快速掌握 ASP.NET 身份认证框架 Identity - 用户注册

推荐关注「码侠江湖」加星标&#xff0c;时刻不忘江湖事这是 ASP.NET Core Identity 系列的第二篇文章&#xff0c;上一篇文章介绍了 Identity 框架的集成&#xff0c;以及一些基础知识。这篇文章讲一讲如何在 ASP.NET Core Identity 中实现用户注册。点击上方或后方蓝字&#…

【GIS风暴】30米分辨率地表覆盖数据GlobeLand30原始数据集简介及下载地址

数据集预览&#xff1a; GlobeLand30是30米空间分辨率全球地表覆盖数据&#xff0c;目前可供下载使用的有3年的数据&#xff1a;2000-2010-2020&#xff0c;本文主要讲述GlobeLand30的官网下载地址和数据集简介。 数据处理方法、成果数据下载&#xff1a; 【ArcGIS风暴】ArcGI…

一、基础折线图详解《手把手教你 ECharts 数据可视化详解》

注&#xff1a;本系列教程需要对应 JavaScript 、html、css 基础&#xff0c;否则将会导致阅读时困难&#xff0c;本教程将会从 ECharts 的官方示例出发&#xff0c;详解每一个示例实现&#xff0c;从中学习 ECharts 。 ECharts 官方示例&#xff1a;https://echarts.apache.o…

NLog自定义Target之MQTT

NLog是.Net中最流行的日志记录开源项目(之一)&#xff0c;它灵活、免费、开源官方支持文件、网络(TCP、UDP)、数据库、控制台等输出社区支持Elastic、Seq等日志平台输出实时日志需求在工业物联网等特定场景下需要实时获取日志信息工业物联网领域常用的是mqtt协议那我们就使用NL…

2016-1-27

2019独角兽企业重金招聘Python工程师标准>>> 1.前端的三大技能:1.1.描述网页内容html 1.2.描述网页样式css 1.3.描述网页行为js2.html和jsp区别在于静态和动态..bootsharp是目前比较火爆的css..angular是目前比较火爆的js.3.单点登陆(SSO):登陆一次就可以访问所有相…

【ArcGIS风暴】ArcGIS生成GlobeLand30土地利用数据集中国区域行列号shp格式对照图(附shp下载)

效果预览: 本文主要讲述了在ArcGIS中生成GlobeLand中国区域对照行列号的shp格式矢量数据,用途在于将自己的研究区跟行列号矢量图层直接叠加显示,快速找出自己所需要的图幅号,便于快速下载数据。同时为了方便使用,本文提供了对照图的下载。 文章目录 1. 创建文件数据库2. 创…

Android 节操视频播放器jiecaovideoplayer自定义播放音频使用:屏蔽全屏按钮,增加倒计时,当前时间/总时间

一、屏蔽全屏按钮 找到JCVideoPlayerStandard.java文件中的代码&#xff1a; private void fixAudio() {if (SrcType.equalsIgnoreCase("Audio")) {//如果是音频&#xff0c;始终显示coverImageView//thumbImageView.setVisibility(View.VISIBLE);coverImageView.se…

一、Qt初尝试,做一个QT计算器《QT 入门到实战》

学习目标 了解 qt 的基本信息了解 qt 的下载及安装了解创建一个基本 qt 项目的流程了解信号与槽通过示例了解信号与槽的设置与编写了解控件添加的方式了解控件如何使用代码获取其文本了解控件如何使用代码设置其文本使用 connect 自定义信号与槽了解使用样式修饰控件外观了解使…

VS C#语言获取输入名称的汉语拼音简拼码和全拼码完整案例教程

结果预览: 扩展阅读: SQL语言获取拼音码:SQL Server编写函数获取汉字的拼音码(简拼) 文章目录 1. 拼音码类编写2. 界面设计3. 前端调用4. 结果展示1. 拼音码类编写 打开Visual Studio,新建一个Winform项目,再添加一个类文件,命名为PYM。 键入如下代码: using Syst…

SSIS 执行变量中的脚步输出列顺序与SQL查询列顺序不同

这个问题是朋友遇到的&#xff0c;做一个SSIS的程序将数据导入到txt。然后再用Oracle的工具导入到Oracle。但是在SSIS中执行变量脚步的时候&#xff0c;发现输出的列名称跟查询的列名称完全不同。比如Schema_id在查询的第三列&#xff0c;但是输出的时候到了第6列。 如图&#…

【ArcGIS风暴】ArcGIS自定义坐标系统案例教程---以阿尔伯斯投影(Albers)为例

在实际工作中,经常需要进行矢量数据或栅格数据的投影转换工作,但有时候ArcGIS中恰恰没有我们需要的坐标系,此时,就需要我们自定义坐标系。本文以阿尔伯斯投影(Albers)为例,讲解自定义投影的一般过程及注意事项。 文章目录 1. 确定投影名称2. 选择投影坐标系及修改参数4.…