一.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正確獲取到了咱們添加的數據。