日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/10 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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有兩個實現方式,一種是NullAbpSession,NullAbpSession是空對象設計模式,用于屬性注入時,在構造函數中對其初始化。 另一種是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

閱罷此文,如果您覺得本文不錯并有所收獲,請【打賞】或【推薦】,也可【評論】留下您的問題或建議與我交流。 你的支持是我不斷創作和分享的不竭動力!

總結

以上是生活随笔為你收集整理的ABP入门系列(10)——扩展AbpSession的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。