正文转由。Github-LearningMpaAbp

正文转自:http://www.cnblogs.com/savorboard/p/aspnetcore-identity.html

ABP入门系列目录——学习Abp框架的实操演练
源码路径:Github-LearningMpaAbp

前言

在 ASP.NET Core 中,仍然沿用了 ASP.NET里面的 Identity
组件库,负责对用户之位置进行求证,总体来说吧,没有MVC 5
之中那复杂,因为以MVC
5里面引入了OWIN的事物,所以广大初家在念书来充分伤脑筋,对于 Identity
都是一头雾水,包括我啊是,曾经在学 identity
这个事物前后花了一个几近月份来弄懂里面的规律。所以大部分开发者对于 Identity
并无爱,也并不曾采取它,会看给绑票。

值得庆幸的凡,在 ASP.NET Core
中,由于对模块的抽象化逐渐清晰,以及中间件的使用,这使得 Identity
的就学和动用路线变得更平易近人,下面就于咱们一起来探望吧。


Getting Started

每当始发前,让咱先行忘记她和Entity Framework的涉,也忘记她与Authentication的涉嫌,我们先修几单英语单词。

起这样几只“单词”你或得整治明白:

# 1: Claims

大家应都知身份证长什么则的,如下:

图片 1

其中,姓名:奥巴马;性别:男;民族:肯尼亚;出生:1961.08.04,等等这些位置信息,可以观看都是一个一个之键值对,那若我们怀念以程序中满怀这些东西,怎么样来计划也?对,你也许想到了动用一个字典进行仓储,一个Key,一个Value刚好满足需求。但是Key,Value的说话感到不极端对劲儿,不顶面向对象,所以若我们做成一个对象的话语,是勿是再次好有啊?最起码你可就此vs的智能提醒了咔嚓,我们修改一下,改化下面这样:

//我给对象取一个名字叫`Claim`你没有意见吧
public class Claim
{
    public string ClaimType { get; set; }

    public string ClaimValue { get; set; }
}

ClaimType
就是Key,ClaimValue就意味着一个Value。这样的话,刚好可以储存一个键值对。这时候姓名:奥巴马是未是好抱上了。

微软的食指万分近,给咱准备了一些默认的ClaimType否?很多常用之还于中间为,一起看吧:

此拉开第一个知识点:ClaimTypes

图片 2

为看经验,截图我单放了同一组成部分哦。可以见到有什么Name,Email,Gender,MobilePhone等常用之且曾闹矣,其他的还有众多。细心的读者或许注意了,它的命名空间是System.Security.Claims,那便证明这事物是.net
框架的同组成部分,嗯,我们小仅需要了解这样多就是OK了。

Claim
介绍完,是休是异常简单,其他地方怎么翻译我弗随便,在本篇文章中,它于
证单元”。

# 2: ClaimsIdentity

在有了“证单元”之后,我们就算用其好打造一摆身份证了,那么相应什么做也?有些同学也许已经想到了,对,就是新建一个目标,然后以构造函数里面把身份证单元传输上,然后就是赢得平等摆身份证了。我们吃当下张位置证取一个英文名字叫
ClaimsIdentity”,这个名字看起还死符合的,既来 Claims
表示该部分,又发出表示该用途的 Identity(身份),很好听的一个名。

实际,在现实生活中,我们的身份证有一对消息是暗藏的,有一些凡是得直接看出的。比如新一代之身份证里面储存了而的指印信息若是看不到的,这些都存储在身份证里面的芯片中,那会见到底准姓名啊,年龄呀等。我们在统筹一个目标的时段也是千篇一律,需要暴露出有东西,那这里我们的
ClaimsIdentity 就爆出出一个 Name,Lable等。

俺们前往的身份证(ClaimsIdentity)还有一个要之性就是种类(AuthenticationType),等等,AuthenticationType是什么东西?看起有些眼熟的法。我们懂得我们和好的身份证是干嘛的吧,就是用来证明我们的位置的,在公作证身份显得其的时,其实她产生不行多种形式载体的,什么意思呢?比如你可直接用出实体形式之身份证,那吧堪是纸张形式的影印件,也可是电子形式的电子码等等,这个时候即便用出一个能代表该在形式的色字段,对,这个AuthenticationType就是事关这个工作的。

下一场我们当给我们的身份证添加一些润色,让该扣留起好看,比如提供一些计上加
Claims 的,删除
Claims的,写到第二上制流里面的哎之类,最终我们的身份证对象看起差不多是这样了:

public class ClaimsIdentity
{
    public ClaimsIdentity(IEnumerable<Claim> claims){}

    //名字这么重要,当然不能让别人随便改啊,所以我不许 set,除了我儿子跟我姓,所以是 virtual 的
    public virtual string Name { get; }
    public string Label { get; set; }

    //这是我的证件类型,也很重要,同样不许 set
    public virtual string AuthenticationType { get; }

    public virtual void AddClaim(Claim claim);

    public virtual void RemoveClaim(Claim claim);

    public virtual void FindClaim(Claim claim);
}

嗯,到这里,我们的身份证关押起如十分全面了,但是打面向对象的角度来说好像还不见了接触啊东西?
对~,还是抽象,我们要抽象出来一个接口来拓展局部羁绊,约束什么也?既然作为一个证书,那么势必会波及到当时几乎只属性信息:
1、名字。2、类型。3、证件是否合法。
反应到接口里面的口舌就是是之类,我们吃接口取单名字被:“身份(IIdentity)”:

此拉开第二独知识点:IIdentity接口。

// 定义证件对象的基本功能。
public interface IIdentity
{
    //证件名称
    string Name { get; }

    // 用于标识证件的载体类型。
    string AuthenticationType { get; }

    //是否是合法的证件。
    bool IsAuthenticated { get; }
}

为此我们的 ClaimsIdentity 最终看起定义就是是这样的了:

public class ClaimsIdentity : IIdentity
{
    //......
}

ClaimsIdentity
介绍了,是免是意识吗非常简单,其他地方怎么翻译我非任,在本篇文章里,它让
身份证”。

# 3: ClaimsPrincipal

出了身份证,我们就能说明本人便是自己了,有些时候一个人数产生成千上万布置身份证,你猜猜是人口是干嘛的?
对,不是黄牛就是行骗犯。

可是,有些上一个人还发出另外甚多种身份,你怀疑者人是干嘛的?这便生正规了针对性怪,比如您可以又是一样号称导师,母亲,商人。如果您想说明你而发出就几乎栽身份的时段,你恐怕要出示教师证,你孩子的出生证,法人代表的营业执照证。

每当先后中,一个身份证不仅仅意味着你这人口了,而是表示一个位置,是印证您自己之要地位哦。如果一个人数还时有发生其它大多种位,这个时节即便需要发出一个东西(载体)来带走着这些证明了对吧?OK,我们叫急需携证明的之目标获得一个贴切点的名字,叫“证书当事人(ClaimsPrincipal)”吧。

以下是 Principal 这个单词在词典给来之讲,我之所以她若应有无眼光吧:

principal  ['prɪnsəpl]  
adj. 主要的;资本的
n. 首长;校长;资本;当事人

这个时节恐怕产生同学会问了,是无是当吃ClaimsIdentityPrincipal于好为?嗯,我哉看当受
ClaimsIdentityPrincipal
可能再度好一些,或许微软的人数偷懒了,简写成了ClaimsPrincipal

亮其效果后,代码就那个好写了,和方面ClaimsIdentity一样的套路:

public class ClaimsPrincipal 
{
    //把拥有的证件都给当事人
    public ClaimsPrincipal(IEnumerable<ClaimsIdentity> identities){}

    //当事人的主身份呢
    public virtual IIdentity Identity { get; }

    public virtual IEnumerable<ClaimsIdentity> Identities { get; }

    public virtual void AddIdentity(ClaimsIdentity identity);

    //为什么没有RemoveIdentity , 留给大家思考吧?
}

立人拘禁起吧几完美了,但是我们还欲针对其抽象一下,抽象哪些东西吧?
作为一个当事人,你当来一个主身份吧,就是您的身价证咯,可能而还会就此到角色(角色背后会详细介绍,这里您懂得有诸如此类个东西便行了)。

这里拉开第三个知识点:IPrincipal 接口。

public interface IPrincipal
{
    //身份
    IIdentity Identity { get; }

    //在否属于某个角色
    bool IsInRole(string role);
}

然后,我们的 证明当事人 看起应当是这么的:

public class ClaimsPrincipal : IPrincipal 
{
   //...
}

ClaimsPrincipal 介绍了了,也充分粗略吧?
其他地方怎么翻译我非任,在本篇文章中,它让 “证件当事人”。

相思以,我们已经知道了 “证件单元(Claims)” , “身份证(ClaimsIdentity)”

“证件当事人(ClaimsPrincipal)”,并且整理清楚了她们中间的逻辑关系,趁热打铁,下面这图是一个identity登入部分的不完全示意图,虚线圈出的一对该好看懂了吧:

图片 3

足看出,首先我们以app这边有一对证明单元,然后调用ClaimsIdentity将证件单元初始化为一个身份证,然后又将身份证交给证书当事人由于其担保。

才将 Getting Started
写了,发现都这么长了,所以打算写成一个文山会海了,可能3 – 4篇吧。

一、AbpSession是Session吗?

总结

好了,本篇就先介绍到这里,在本篇博客中,我们学会了几单英文单词,并且了解了这些英文单词在程序中凡是扮演这如何一个目标。并且根据图我们领略了这些目标在总体认证系统种处在何等一个职。
我发现要想将 identity
讲明白不过因就同一首博客是不够的,下同样篇我们以针对.NET Authentication中档件进行抽丝剥茧,直到掌握.NET的通认证体系后,我们更来拘禁一下
Identiy 到底和 Entity Framework 有着怎样的爱恨情仇。

顿时只有是一个开,大家如果看本篇博客对你发出帮扶的话,感谢您的【推荐】,如果你针对
.NET Core 感兴趣可以关注自身,我会定期以博客分享有关 .NET Core
的学习心得。


本文地址:http://www.cnblogs.com/savorboard/p/aspnetcore-identity.html
作者博客:Savorboard
欢迎转载,请在肯定位置被出出处及链接

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身份证明

本节关键参照自博客园Savorboard的博文,在此感谢Savorboard的不错分享,建议大家去细细品读一番:

ASP.NET Core 之 Identity
入门(一)
ASP.NET Core 之 Identity
入门(二)
ASP.NET Core 之 Identity
入门(三)

1、Cliam(身份信息)

将身份证举例,其中包括姓名:奥巴马、性别:男、民族:xx、出生:xx、住址:xx、公民省份号码:xxx,这些键值对都是位信息。其中姓名、性别、民族、出生、住址、公民省份号码这些是身价信息种类(ClaimsType),微软曾被我们预定义了平等多重之身份信息类,其中包(Email、Gender、Phone等等)。
图片 4

2、ClaimsIdentity(身份证)

生了身份信息,一组装,不就改为了身份证。
扣押下ClaimsIdentity的简单代码:

public class ClaimsIdentity: IIdentity
{
    public virtual IEnumerable<Claim> Claims
    {
    get {   //省略其他代码  }
    }

    //名字这么重要,当然不能让别人随便改啊,所以我不许 set,除了我儿子跟我姓,所以是 virtual 的
    public virtual string Name { get; }

    //这是我的证件类型,也很重要,同样不许 set
    public 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的略登陆流程:

图片 5

起当下张图来拘禁,我们登陆的时提供部分身价信息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)啊!!!

图片 6

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

季、开始扩张AbpSession(第一栽办法:推荐)

达成同一节就经营清矣思路,这无异于省咱们就算撸起袖子扩展吧。
今天要是我们要扩大一个Email属性:

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

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

图片 7

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、无图无真相

图片 8

图片 9


总结:

本文首先针对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

相关文章