ABP入门系列(10)——扩展AbpSession

一、AbpSession是Session吗?

1、首先来看看它们分别对应的类型是什么?

查看源码发现Session是定义在Controller中的类型为HttpSessionStateBase的属性。 public HttpSessionStateBase Session { get; set; }

再来看看AbpSession是何须类也,咱们定位到AbpController中看一看。 public IAbpSession AbpSession { get; set; }

好吧,原来AbpSession是IAbpSession类型啊。但这就可以断定AbpSession不是Session吗? 未必吧,如果IAbpsession的具体实现中还是依赖Session也不一定哦,如果是这样,那AbpSession可以算作Session的扩展,也可以说是Session。 咱还是找找IAbpsession的具体实现一探究竟吧。 Abp中对IAbpsession有两个实现方式,一种是NullAbpSessionNullAbpSession是空对象设计模式,用于属性注入时,在构造函数中对其初始化。 另一种是ClaimsAbpSession,咱们来一探究竟。

2、一探究竟ClaimsAbpSession

以下代码是ClaimsAbpSession的节选:

/// <summary>
/// Implements <see cref="IAbpSession"/> to get session properties from claims of <see cref="Thread.CurrentPrincipal"/>.
/// </summary>
public class ClaimsAbpSession : IAbpSession, ISingletonDependency
{public virtual long? UserId{get{var userIdClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);if (string.IsNullOrEmpty(userIdClaim?.Value)){return null;}long userId;if (!long.TryParse(userIdClaim.Value, out userId)){return null;}return userId;}}public IPrincipalAccessor PrincipalAccessor { get; set; }public ClaimsAbpSession(IMultiTenancyConfig multiTenancy){MultiTenancy = multiTenancy;PrincipalAccessor = DefaultPrincipalAccessor.Instance;}
}

其中IPrincipalAccessor又是什么鬼,从构造函数来看,DefaultPrincipalAccessor应该是个单例模式。

public class DefaultPrincipalAccessor : IPrincipalAccessor, ISingletonDependency
{public virtual ClaimsPrincipal Principal => Thread.CurrentPrincipal as ClaimsPrincipal;public static DefaultPrincipalAccessor Instance => new DefaultPrincipalAccessor();
}

其中public static DefaultPrincipalAccessor Instance => new DefaultPrincipalAccessor();是属性表达式写法,相当于:

public static DefaultPrincipalAccessor Instance 
{    get { new DefaultPrincipalAccessor();}
}

所以并非是单例模式(长了个记性,并不是定义了Instance属性的就是单例)

将上面两部分代码一中和,AbpSession中的UserId不就是这样获得的: ((ClaimsPrincipal)Thread.CurrentPrincipal).Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);

好了一切一目了然了,AbpSession最终依赖的是ClaimsPrincipal,并不是Session

所以AbpSession不是Session!!! 所以AbpSession不是Session!!! 所以AbpSession不是Session!!!

ClaimsPrincipal又是什么鬼?我就喜欢你这打破砂锅问到底的劲,且听我娓娓道来。

二、Identity身份认证

1、Cliam(身份信息)

拿身份证举例,其中包括姓名:奥巴马、性别:男、民族:xx、出生:xx、住址:xx、公民省份号码:xxx,这些键值对都是身份信息。其中姓名、性别、民族、出生、住址、公民省份号码这些是身份信息类别(ClaimsType),微软已经给我们预定义了一系列的身份信息类别,其中包括(Email、Gender、Phone等等)。

2、ClaimsIdentity(身份证)

有了身份信息,一组装,不就成了身份证。 看下ClaimsIdentity的简要代码:

public class ClaimsIdentity: IIdentity
{public virtual IEnumerable<Claim> Claims{get {   //省略其他代码  }}//名字这么重要,当然不能让别人随便改啊,所以我不许 set,除了我儿子跟我姓,所以是 virtual 的public virtual string Name { get; }//这是我的证件类型,也很重要,同样不许 setpublic virtual string AuthenticationType { get; }public virtual void AddClaim(Claim claim);public virtual void RemoveClaim(Claim claim);public virtual void FindClaim(Claim claim);
}

可以看到ClaimsIdentity维护了一个Claim枚举列表。 其中AuthenticationType,从字面意思理解是验证类型。什么意思呢?比如我们拿身份证去政府部门办理业务时,有时需要持本人身份证,但有时候需要身份证复印件即可。

3、ClaimsPrincipal (证件所有者)

我们用身份信息构造了一个身份证,这个身份证肯定是属于具体的某个人吧。 所以ClaimsPrincipal就是用来维护一堆证件的。 因为现实生活中也是这样,我们有身份证、银行卡、社保卡等一系列证件。 那咱们就来看.net中是怎样实现的:

//核心代码部分
public class ClaimsPrincipal :IPrincipal
{//把拥有的证件都给当事人public ClaimsPrincipal(IEnumerable<ClaimsIdentity> identities){}//当事人的主身份呢public virtual IIdentity Identity { get; }public virtual IEnumerable<ClaimsIdentity> Identities { get; }public virtual void AddIdentity(ClaimsIdentity identity);//为什么没有RemoveIdentity , 留给大家思考吧?
}

了解了这些概念,我们再来看看Identity的简要登陆流程:

从这张图来看,我们登陆的时候提供一些身份信息Claim(用户名/密码),然后Identity中间件根据这些身份信息构造出一张身份证ClaimsIdentity,然后把身份证交给ClaimsPrincipal证件所有者保管。

三、捋一捋Abp中的登陆流程

定位到AccountController,关注下以下代码:

[HttpPost]
[DisableAuditing]
public async Task<JsonResult> Login(LoginViewModel loginModel, string returnUrl = "", string returnUrlHash = "")
{CheckModelState();var loginResult = await GetLoginResultAsync(loginModel.UsernameOrEmailAddress,loginModel.Password,loginModel.TenancyName);await SignInAsync(loginResult.User, loginResult.Identity, loginModel.RememberMe);if (string.IsNullOrWhiteSpace(returnUrl)){returnUrl = Request.ApplicationPath;}if (!string.IsNullOrWhiteSpace(returnUrlHash)){returnUrl = returnUrl + returnUrlHash;}return Json(new AjaxResponse { TargetUrl = returnUrl });
}private async Task<AbpLoginResult<Tenant, User>> GetLoginResultAsync(string usernameOrEmailAddress, string password, string tenancyName)
{var loginResult = await _logInManager.LoginAsync(usernameOrEmailAddress, password, tenancyName);switch (loginResult.Result){case AbpLoginResultType.Success:return loginResult;default:throw CreateExceptionForFailedLoginAttempt(loginResult.Result, usernameOrEmailAddress, tenancyName);}
}private async Task SignInAsync(User user, ClaimsIdentity identity = null, bool rememberMe = false)
{if (identity == null){identity = await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);}AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = rememberMe }, identity);
}

分析发现主要包括以下几个步骤: 1、 GetLoginResultAsync --> loginManager.LoginAsync --> userManager.CreateIdentityAsync:不要以为调用了LoginAsync就以为是登录,其实这是伪登录。主要根据用户名密码去核对用户信息,构造User对象返回,然后再根据User对象的身份信息去构造身份证(CliamsIdentity)。 2、SignInAsync --> AuthenticationManager.SignOut -->AuthenticationManager.SignIn : AuthenticationManager(认证管理员),负责真正的登入登出。SignIn的时候将第一步构造的身份证(CliamsIdentity)交给证件所有者(ClaimsPrincipal)。

是不是明白该怎么扩展AbpSession了? 关键是往身份证(CliamsIdentity)中添加身份信息(Cliam)啊!!!

其实去github上Abp官网搜issue,发现土耳其大牛也是给的这种扩展思路,详参此链

四、开始扩展AbpSession(第一种方式:推荐)

上一节已经理清了思路,这一节咱们就撸起袖子扩展吧。 现在假设我们需要扩展一个Email属性:

1、登录前添加Cliam(身份信息)

定位到AccountController,修改SignInAsync方法,在调用AuthenticationManager.SignIn之前添加下面代码: identity.AddClaim(new Claim(ClaimTypes.Email, user.EmailAddress));

2、定义IAbpSession扩展类获取扩展属性

既然只要我们在登录的时候通过在身份信息中添加要扩展的属性,我们就可以通过ClaimsPrincipal中获取扩展的属性。 所以我们可以通过对IAbpSession进行扩展,通过扩展方法从CliamsPrincipal中获取扩展属性。

所以我们需要在领域层,也就是.Core结尾的项目中对IAbpSession进行扩展。定位到.Core结尾的项目中,添加Extensions文件夹,添加扩展类AbpSessionExtension2

namespace LearningMpaAbp.Extensions
{/// <summary>/// 通过扩展方法来对AbpSession进行扩展/// </summary>public static class AbpSessionExtension2{public static string GetUserEmail(this IAbpSession session){return GetClaimValue(ClaimTypes.Email);}private static string GetClaimValue( string claimType){var claimsPrincipal = DefaultPrincipalAccessor.Instance.Principal;var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType);if (string.IsNullOrEmpty(claim?.Value))return null;return claim.Value;}}
}

通过扩展类,我们不需要做其他额外的更改,即可通过ApplicationService, AbpController 和 AbpApiController 这3个基类已经注入的AbpSession属性调用GetUserEmail()来获取扩展的Email属性。

这种方式时最简单的方式,推荐此种方法!!!

五、开始扩展AbpSession(第二种方式)

ApplicationService, AbpController 和 AbpApiController 这3个基类已经注入了AbpSession属性。 所以我们需要在领域层,也就是.Core结尾的项目中对AbpSession进行扩展。 现在假设我们需要扩展一个Email属性。

1、扩展IAbpSession

定位到.Core结尾的项目中,添加Extensions文件夹,然后添加IAbpSessionExtension接口继承自IAbpSession

namespace LearningMpaAbp.Extensions
{public interface IAbpSessionExtension : IAbpSession{string Email { get; }}
}

2、实现IAbpSessionExtension

添加AbpSessionExtension类,继承自ClaimsAbpSession并实现IAbpSessionExtension接口。

namespace LearningMpaAbp.Extensions
{public class AbpSessionExtension : ClaimsAbpSession, IAbpSessionExtension{public AbpSessionExtension(IMultiTenancyConfig multiTenancy) : base(multiTenancy){}public string Email => GetClaimValue(ClaimTypes.Email);private string GetClaimValue(string claimType){var claimsPrincipal = PrincipalAccessor.Principal;var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType);if (string.IsNullOrEmpty(claim?.Value))return null;return claim.Value;}}
}

3、替换掉注入的AbpSession属性

先来替换掉AbpController中注入的AbpSession 定位到.Web\Controllers\xxxxControllerBase.cs,使用属性注入IAbpSessionExtension。添加以下代码:

//隐藏父类的AbpSession
public new IAbpSessionExtension AbpSession { get; set; }

再来替换掉ApplicationService中注入的AbpSession 定位到.Application\xxxxAppServiceBase.cs。使用属性注入IAbpSessionExtension,同样添加以下代码:

//隐藏父类的AbpSession
public new IAbpSessionExtension AbpSession { get; set; }

至于AbpApiController要不要替换AbpSession,就视情况而定了,如果你使用的是Abp提供的动态WebApi技术,就不需要替换了,因为毕竟最终调用的是应用服务层的Api。如果WebApi是自己代码实现的,那就仿照上面自行替换吧,就不罗嗦了。

很显然,这种方式教第一种方式要麻烦许多。。。

4、无图无真相

总结:

本文首先对AbpSession一探真面目,了解到AbpSession不是Session; 然后对Identity身份认证流程就行简要剖析,发现AbpSession是依赖于ClaimsPrincipal,从而确定扩展AbpSession的思路:关键是往身份证(CliamsIdentity)中添加身份信息(Cliam)啊!!!; 最终提供了两种扩展思路: 其中推荐通过对IAbpSession进行扩展,通过扩展方法从CliamsPrincipal中获取扩展属性。 本文参考了以下博文,在此再次感谢它们的精彩分享:

ASP.NET Core 之 Identity 入门(一)--Savorboard ASP.NET Core 之 Identity 入门(二)--Savorboard ASP.NET Core 之 Identity 入门(三)--Savorboard Asp.net Boilerplate之AbpSession扩展--kid1412 基于DDD的.NET开发框架 - ABP Session实现--Joye.Net

阅罢此文,如果您觉得本文不错并有所收获,请【打赏】或【推荐】,也可【评论】留下您的问题或建议与我交流。 你的支持是我不断创作和分享的不竭动力!

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

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

相关文章

太吾绘卷第一世攻略_耽美推文-BL-仿佛在攻略一只河豚

目录&#xff1a;《全能攻略游戏》by公子如兰《无限升级游戏》by暗夜公主《无限游戏》BY SISIMO《请听游戏的话》by木兮娘《游戏&#xff0c;在线直播》by雨田君《最强游戏制作人》by木兰竹《在逃生游戏里撩宿敌》by临钥《游戏加载中》by龙柒《狩猎游戏》by砯涯《当异性参加逃生…

ABP入门系列(11)——编写单元测试

1. 前言 In computer programming, unit testing is a software testing method by which individual units of source code, sets of one or more computer program modules together with associated control data, usage procedures, and operating procedures, are tested …

etl构建数据仓库五步法_带你了解数据仓库的基本架构

数据仓库的目的是构建面向分析的集成化数据环境&#xff0c;为企业提供决策支持&#xff08;Decision Support&#xff09;。其实数据仓库本身并不“生产”任何数据&#xff0c;同时自身也不需要“消费”任何的数据&#xff0c;数据来源于外部&#xff0c;并且开放给外部应用&a…

ABP入门系列(12)——如何升级Abp并调试源码

1. 升级Abp 本系列教程是基于Abp V1.0版本&#xff0c;现在Abp版本已经升级至V1.4.2&#xff0c;其中新增了New Feature&#xff0c;并对Abp做了相应的Enhancements&#xff0c;以及Bug fixs。现在我们就把它升级至最新版本&#xff0c;那如何升级呢&#xff1f; 下面就请按我…

聚类分析在用户行为中的实例_看完这篇,你还敢说不懂聚类分析?

点击上方蓝色字关注我们~大数据分析中的应用&#xff0c;最常用的经典算法之一就是聚类法&#xff0c;这是数据挖掘采用的起步技术&#xff0c;也是数据挖掘入门的一项关键技术。什么是聚类分析&#xff1f;聚类分析有什么用&#xff1f;聚类算法有哪些&#xff1f;聚类分析的应…

ABP入门系列(13)——Redis缓存用起来

1. 引言 创建任务时我们需要指定分配给谁&#xff0c;Demo中我们使用一个下拉列表用来显示当前系统的所有用户&#xff0c;以供用户选择。我们每创建一个任务时都要去数据库取一次用户列表&#xff0c;然后绑定到用户下拉列表显示。如果就单单对一个demo来说&#xff0c;这样实…

ABP入门系列(14)——应用BootstrapTable表格插件

1. 引言 之前的文章ABP入门系列&#xff08;7&#xff09;——分页实现讲解了如何进行分页展示&#xff0c;但其分页展示仅适用于前台web分页&#xff0c;在后台管理系统中并不适用。后台管理系统中的数据展示一般都是使用一些表格插件来完成的。这一节我们就使用BootstrapTab…

musictools怎么用不了_夏天少不了一只草编包,怎么搭配才不像“买菜用”?

要说有什么包包能跟夏天的气息一拍即合&#xff0c;那绝对非“草编包”莫属&#xff01;由藤条、柳条等编制而成的田园风“小篮子”&#xff0c;已经成了夏日街头小姐姐们的包包首选。各大品牌都争相推出草编包系列&#xff0c;在原有的浪漫度假风之外&#xff0c;添加了更多时…

ABP入门系列(15)——创建微信公众号模块

1. 引言 现在的互联网已不在仅仅局限于网页应用&#xff0c;IOS、Android、平板、智能家居等平台正如火如荼的迅速发展&#xff0c;移动应用的需求也空前旺盛。所有的互联网公司都不想错过这一次移动浪潮&#xff0c;布局移动市场分一份移动红利。 的确&#xff0c;智能手机作…

spark 算子例子_Spark性能调优方法

公众号后台回复关键词&#xff1a;pyspark&#xff0c;获取本项目github地址。Spark程序可以快如闪电⚡️&#xff0c;也可以慢如蜗牛?。它的性能取决于用户使用它的方式。一般来说&#xff0c;如果有可能&#xff0c;用户应当尽可能多地使用SparkSQL以取得更好的性能。主要原…

ABP入门系列(16)——通过webapi与系统进行交互

1. 引言 上一节我们讲解了如何创建微信公众号模块&#xff0c;这一节我们就继续跟进&#xff0c;来讲一讲公众号模块如何与系统进行交互。 微信公众号模块作为一个独立的web模块部署&#xff0c;要想与现有的【任务清单】进行交互&#xff0c;我们要想明白以下几个问题&#x…

python嵩天第二版第五章_如何避免从入门到放弃——python小组学习复盘

2019年春节python学习行动复盘2019-02-09为了主攻python&#xff0c;没有参加心理学晨读。对心理学也不敢兴趣&#xff0c;怕耽误学习python的时间。那么没学习心理学的情况下&#xff0c;python学的怎么样&#xff1f;是否达到自己的预期&#xff1f;一、预期目标&#xff1a;…

ABP入门系列(17)——使用ABP集成的邮件系统发送邮件

1.Abp集成的邮件模块是如何实现的 ABP中对邮件的封装主要集成在Abp.Net.Mail和Abp.Net.Mail.Smtp命名空间下&#xff0c;相应源码在此。 分析可以看出主要由以下几个核心类组成&#xff1a; EmailSettingNames&#xff1a;静态常量类&#xff0c;主要定义了发送邮件需要的相关…

cdn转发防攻击_高防CDN和高防服务器的区别?

越来越多的网络攻击需要处理&#xff0c;而高防CDN和高防服务器是很好的选择&#xff0c;那么如何选择呢&#xff1f;我们就来分析一下关于这两者之间的选择。首先从价格上看的话&#xff0c;高防御CDN的价格相对高一些&#xff0c;防御上看&#xff0c;高防御CDN的防御效果也更…

ABP入门系列(18)—— 使用领域服务

1.引言 自上次更新有一个多月了&#xff0c;发现越往下写&#xff0c;越不知如何去写。特别是当遇到DDD中一些概念术语的时候&#xff0c;尤其迷惑。如果只是简单的去介绍如何去使用ABP&#xff0c;我只需参照官方文档&#xff0c;实现到任务清单Demo中去就可以了&#xff0c;…

mysql文件类型_MyCat教程:实现MySql主从复制

原文&#xff1a;http://iii75.cn/mwQhBW 作者&#xff1a;波波烤鸭历史相关文章Mycat入门教程单个mysql数据库在处理业务的时候肯定是有限的&#xff0c;这时我们扩展数据库的第一种方式就是对数据库做读写分离(主从复制),本文我们就先来介绍下怎么来实现mysql的主从复制操作。…

截屏当前界面_电脑屏幕怎么截取,常见的几种电脑截屏方法

随着科技的快速发展电脑已经逐渐渗入到我们的工作和生活中&#xff0c;我们需要使用电脑的地方也越来越多&#xff0c;电脑已经成为了一种新式的办公工具。今天小编不是向大家介绍电脑的应用&#xff0c;而是想要和大家分享一下关于电脑截图的几种方法。1、Print Screen SysRqP…

ABP入门系列(19)——使用领域事件

1.引言 最近刚学习了下DDD中领域事件的理论知识&#xff0c;总的来说领域事件主要有两个作用&#xff0c;一是解耦&#xff0c;二是使用领域事件进行事务的拆分&#xff0c;通过引入事件存储&#xff0c;来实现数据的最终一致性。若想了解DDD中领域事件的概念&#xff0c;可参…

扩容是元素还是数组_Java中对数组的操作

数组对于每一门编程语言来说都是重要的数据结构之一&#xff0c;当然不同语言对于数组的实现及处理也不尽相同。Java语言中提供的数组是用来存储固定大小的同类型元素。如&#xff1a;声明一个数组变量&#xff0c;numbers[100]来代替直接声明100个独立变量number0,number1,...…

ABP入门系列(20)——使用后台作业和工作者

1.引言 说到后台作业&#xff0c;你可能条件反射的想到BackgroundWorker&#xff0c;但后台作业并非是后台任务&#xff0c;后台作业用一种队列且持久稳固的方式安排一些待执行后台任务。 为执行长时间运行的任务而用户无需等待&#xff0c;以提高用户体验。为创建可重试且持…