一.AbpSession的认识设计模式
在ABP中提供了IAbpSession的接口用来获取用户和租户的信息,没有使用Asp.Net中的Session,那么AbpSession到底和Session有没有关系?具体是怎么实现的呢?session
在ABP的源码中共有两个类具体实现了IAbpSession接口:NullAbpSession和ClaimAbpSession,其中NullAbpSession实现了空对象设计模式,那么主要的代码就是在这个ClaimAbpSession中框架
从上面的代码中咱们能够看出咱们的用户ID是经过PrincipalAcessor.Principal获取到的,那么这又是一个什么东西呢?ide
其中ClaimsPricipal是微软提供的用于登录验证的的一个类。从上面的代码中,咱们终于了解到其实AbpSession和Session没有任何关系的。咱们的AbpSession就是从这个类里面获取的相应的信息(用户ID,租户ID,模拟用户Id,模拟租户ID),那么问题来了,这个ClaimsPricipal到底又是一个什么东西呢?this
二.了解Identityspa
1.为何要使用Asp.net Identity,它有什么优点呢?.net
1).做为一种用户角色管理的组件,支持Asp.net MVC、Asp.net Core 、WebApi等微软的框架,能够很方便的集成。其实咱们新建的MVC项目中,已经把Identity的相关组件库引用进去了。设计
2).支持持久化,能够很方便的对接EF和EFCore3d
3).能够很容易的建立角色和用户,管理方便code
4).能够很方方便的和第三方用户登录对接。
2.identity中有三个比较重要的对象。
Claim(证件信息,重点是信息)
简单来讲这个对象就是一个键值对,存放信息用的
上面的type表示的信息的类型,在identity中其实已经提供了一些常常使用的类型的类ClaimTypes
ClaimIdentity(证件)
这个东西存放的就是咱们Claim的集合,经过不少的Claim信息,咱们就能够拼装成咱们的证件ClaimIdentity,好比咱们的身份证,须要提供性别,姓名,出生年月,家庭地址等Claim信息,而后就组装成了咱们的身份证(ClaimIdentity)
其中AuthenticationType就是证件的类型,咱们的证件信息组成的证件能够存在多种展示形式,拿咱们的身份证来讲,有实体证件和电子复印件。还有咱们的车票,能够是电子票,也能够是实体票
ClaimsPrincipal(证件持有者)
这个类中存放了不少的证件ClaimIdentity,它就像一我的,同时能够持有身份证,车票,三好学生证等证件
3.Asp.net Identity的是实例应用
其实在咱们建立咱们的MVC项目的时候,在菜单栏中就存在相应的选项,是否使用身份验证。
接下来我将经过AccountController(帐号登录注册控制器)详细了解下一Identity的机制,在Identity中存在三个重要的Manager对象
1.UserMananger
UserMananger对象用来处理建立用户等逻辑
2.SignManager
SignManager对象用来对用户的登录和登出的逻辑进行处理
3.RoleManager
RoleManager对象用来对用用户角色相关逻辑进行处理。
三.Abp中Claim的使用
从上面的代码中,咱们能够看出在_logInManager.LoginAsync这个方法中,会将咱们的Claim信息构造为证件ClaimIdentity,这个能够在Abp源码中看到。那么咱们的ClaimIdentiy这些证件信息是如何添加到证件持有人ClaimPricipal中的呢?只有将信息存放在ClaimPricipal中,才会有最上面,从ClaimPricipal中获取咱们的用户信息的操做。其实咱们猜一猜能够知道。这个操做确定是在登录的时候,信息存放在ClaimPricipal中的。其实_logInManager.LoginAsync也实现了。可是经过源码中咱们发现,不单单这个方法中实现了往ClaimPricipal中添加信息的,在后面的SignManager.SignAsync中也实现了,这个将是咱们后面扩展AbpSession的关键,具体的源码将在AbpSession扩展中给出。
四.AbpSession的扩展
咱们已经知道AbpSession并非Session,咱们的登录信息没有使用Session的方式存储,而是使用ClaimPrincipal的方式存放的,那么咱们须要额外的存放一些咱们的信息在咱们的Abp系统中,须要怎么作呢?其实很简单啊,不就是我向ClaimPricipal中的添加ClaiIdentity嘛。首先看一下咱们Account中代码
答案是能够的,这个咱们能够从Abp的源码中找到答案。
在扩展前,咱们须要将咱们自定义的Claim信息添加到ClaimIdentity中去,修改AccountController的Login方法
第一种扩展思路:经过对AbpSession添加扩展方法的方式,获取到自定义的Claim
在Core层添加咱们的扩展类 public static class AbpSessionExtension { public static string GetName(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; } } 在Controller中调用 public class HomeController : StudyABPProjectControllerBase { public ActionResult Index() { var name = AbpSession.GetName(); return Content(name); } }
理想是美好的,现实是残酷的,在abp.core项目中,没法获取到咱们自定义的信息,由于DefaulPricinpalAccessor.Instance,就是经过Thread.CurentPrincipal获取的当前的证件拥有者,可是在Core项目中,这个对象始终为null,因此致使没法获取到principal。
第二种扩展思路:直接修改原有AbpSession,使用咱们本身定义的AbpSession覆盖原来的,而后在咱们自定义的AbpSession中添加咱们自定义的属性
1.首先建立咱们的MyAbpSession和IMyAbpSession
public class MyAbpSession : ClaimsAbpSession, IMyAbpSession { public MyAbpSession(IPrincipalAccessor principalAccessor, IMultiTenancyConfig multiTenancy, ITenantResolver tenantResolver, IAmbientScopeProvider<SessionOverride> sessionOverrideScopeProvider) : base(principalAccessor, multiTenancy, tenantResolver, sessionOverrideScopeProvider) { } private string GetClaimValue(string claimType) { var claimsPrincipal = this.PrincipalAccessor.Principal; var claim = claimsPrincipal?.Claims.FirstOrDefault(c => c.Type == claimType); if (string.IsNullOrEmpty(claim?.Value)) return null; return claim.Value; } public string MyName => GetClaimValue("MyName"); } public interface IMyAbpSession:IAbpSession { string MyName { get; } }
2.在UI层和Application层覆盖原来的AbpSession
public abstract class StudyABPProjectControllerBase: AbpController { public new IMyAbpSession AbpSession { get; set; } protected StudyABPProjectControllerBase() { LocalizationSourceName = StudyABPProjectConsts.LocalizationSourceName; } protected void CheckErrors(IdentityResult identityResult) { identityResult.CheckErrors(LocalizationManager); } }
直接运行咱们代码
OK,咱们的AbpSession经过重写原来的AbpSession正确获取到了咱们添加的数据。