一.AbpSession的認識
在ABP中提供了IAbpSession的接口用來獲取用戶和租戶的信息,沒有使用Asp.Net中的Session,那么AbpSession到底和Session有沒有關系?具體是怎么實現的呢?

在ABP的源碼中共有兩個類具體實現了IAbpSession接口:NullAbpSession和ClaimAbpSession,其中NullAbpSession實現了空對象設計模式,那么主要的代碼就是在這個ClaimAbpSession中

從上面的代碼中我們可以看出我們的用戶ID是通過PrincipalAcessor.Principal獲取到的,那么這又是一個什么東西呢?



其中ClaimsPricipal是微軟提供的用於登陸驗證的的一個類。從上面的代碼中,我們終於了解到其實AbpSession和Session沒有任何關系的。我們的AbpSession就是從這個類里面獲取的相應的信息(用戶ID,租戶ID,模擬用戶Id,模擬租戶ID),那么問題來了,這個ClaimsPricipal到底又是一個什么東西呢?
二.了解Identity
1.為什么要使用Asp.net Identity,它有什么優勢呢?
1).作為一種用戶角色管理的組件,支持Asp.net MVC、Asp.net Core 、WebApi等微軟的框架,可以很方便的集成。其實我們新建的MVC項目中,已經把Identity的相關組件庫引用進去了。
2).支持持久化,可以很方便的對接EF和EFCore
3).可以很容易的創建角色和用戶,管理方便
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正確獲取到了我們添加的數據。
