1. 程式人生 > >吐槽一下Abp的使用者和租戶管理模組

吐槽一下Abp的使用者和租戶管理模組

![](https://img2020.cnblogs.com/blog/587720/202010/587720-20201030170353082-454066264.jpg) ## 1. 背景 ASP.NET Core 基於宣告的訪問控制到底是什麼鬼? 聊到基於宣告的身份認證將 身份和簽發機構分離,應用程式信任簽發機構,故認可簽發的身份資訊。 | -- | --- | --- | --- | | --- | --- | --- | --- | | Claim | B站:438962688 Name:飯思思_ | weibo:538210234 Name:飯思思van | 姓名:不詳 籍貫:九江| | ClaimsIdentity | 嗶哩嗶哩賬戶 | 微博賬戶 | 身份證 | | ClaimsPrincipal | | 於是我們通常會有如下: ``` var claims = new[] { new Claim(nameof(ClaimTypes.NameIdentifier),_authData.Data["userId"].ToString(),ClaimValueTypes.String), new Claim(nameof(ClaimTypes.Name),_authData.Data["userName"].ToString(),ClaimValueTypes.String), new Claim("profileId",_authData.Data["profileId"].ToString()), new Claim("positionId",_authData.Data["positionId"].ToString()), new Claim("organizationId",_authData.Data["organizationId"].ToString()), new Claim("maxAge",_authData.Data["maxAge"].ToString()), }; // 設定身份卡片內容 、身份卡片核心Name, 這個時候HttpContext.User var identity = new ClaimsIdentity(claims, Scheme.Name,nameof(ClaimTypes.Name),nameof(ClaimTypes.Role)); Context.User = new ClaimsPrincipal(identity); ``` 我們現在可以在Action中使用 HttpContext.User.Identity 獲取宣告的身份資訊。 當我滿心歡喜在Abp vnext中封裝的`ICurrentUser介面`獲取身份資訊,卻無法獲取身份資訊。 > ICurrentUser 封裝了身份資訊,用於獲取有關當前活動的使用者資訊,已經被Abp框架預設注入。 你會在ApplicationSerive、 AbpController看到只讀屬性CurrentUser, 在Abp服務和控制器中是可以即時使用的。 | --- | --- | | --- | --- | | ![](https://img2020.cnblogs.com/blog/587720/202010/587720-20201030170534572-1269183886.png) | ![](https://img2020.cnblogs.com/blog/587720/202010/587720-20201030170609837-620994448.png) | ## 2. Abp使用者、租戶管理 Abp`ICurrentUser`獲取不到常規HttpContext.User資訊,是因為使用了特定的封裝,封裝的方式我不能苟同: ``` 以下是 ICurrentUser 介面的基本屬性: IsAuthenticated 如果當前使用者已登入(已認證),則返回 true. 如果使用者尚未登入,則 Id 和 UserName 將返回 null. Id (Guid?): 當前使用者的Id,如果使用者未登入,返回 null. UserName (string): 當前使用者的使用者名稱稱. 如果使用者未登入,返回 null. TenantId (Guid?): 當前使用者的租戶Id. 對於多租戶 應用程式很有用. 如果當前使用者未分配給租戶,返回 null. Email (string): 當前使用者的電子郵件地址. 如果當前使用者尚未登入或未設定電子郵件地址,返回 null. Roles (string[]): 當前使用者的角色. 返回當前使用者角色名稱的字串陣列. ..... ``` 這裡面有幾個問題: 1. ICurrentUser將使用者id、租戶TenantId硬編碼為`GUID` 底層產生的身份id、租戶id若不為GUID,則根本不可用。 最差的情況也應該用個泛型,由應用決定特定身份片段的型別。 2. ICurrentUser 修改了`IsAuthenticated`的取值邏輯: - ASP.NET Core官方`認證型別不為空`,就認為使用者認證通過。 ``` // --- 來自asp.netcore原始碼:https://github.com/dotnet/runtime/blob/master/src/libraries/System.Security.Claims/src/System/Security/Claims/ClaimsIdentity.cs public virtual bool IsAuthenticated { get { return !string.IsNullOrEmpty(_authenticationType); } } ..... ``` - Abp官方則認為`UserId不為空`,就認為使用者認證通過。 ``` // ---擷取自abp官方原始碼:Volo.Abp.Users.CurrentUser public class CurrentUser : ICurrentUser, ITransientDependency { private static readonly Claim[] EmptyClaimsArray = new Claim[0]; public virtual bool IsAuthenticated => Id.HasValue; ..... } ``` 3. ICurrentUser修改了`UserName`的取值邏輯: - Asp.NetCore檢索宣告資訊中ClaimType==某個NameClaimType的Claim值, 作為身份認證卡片Identity的Name, 更靈活 - Abp 檢索宣告資訊中ClaimType=="http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"的值,作為身份驗證卡片的Name, 硬編碼 Abp 將UserId、TenantId 硬編碼為GUID,已經不夠通用; 另外Abp強行變更了ASP.NET Core基於宣告的身份驗證的取值邏輯,若要我們接受,需要一點學習成本。 本次我的專案就是因為UserID、TenantId為String, 在CurrentUser中轉換失敗,Name也取值失敗。 這樣我在專案中就無法使用Abp ApplicationService、Controller的CurrentUser只讀屬性。 ## 3. 針對Abp使用者、租戶管理的應對方法 我的策略,還是向儘量使用Abp框架,儘量做到【對修改封閉,對擴充套件開放】, 於是我仿照Abp的CurrentUser實現了適合自身專案的`CurrentUser`: ``` public class CurrentUser: ITransientDependency { private static readonly Claim[] EmptyClaimsArray = new Claim[0]; public virtual string Id => _principalAccessor.Principal?.Claims?.FirstOrDefault(c => c.Type == nameof(ClaimTypes.NameIdentifier))?.Value; public virtual string UserName => _principalAccessor.Principal?.Claims?.FirstOrDefault(c => c.Type == nameof(ClaimTypes.Name))?.Value; public virtual string Email => _principalAccessor.Principal?.Claims?.FirstOrDefault(c => c.Type == nameof(ClaimTypes.Email))?.Value; public virtual string TenantId => _principalAccessor.Principal?.Claims?.FirstOrDefault(c => c.Type == "profileId")?.Value; public virtual string[] Roles => FindClaims("roleId").Select(c => c.Value).ToArray(); private readonly ICurrentPrincipalAccessor _principalAccessor; public CurrentUser(ICurrentPrincipalAccessor principalAccessor) { _principalAccessor = principalAccessor; } public virtual Claim FindClaim(string claimType) { return _principalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == claimType); } } } ``` 編寫繼承自ApplicationService、AbpController的通用服務類、控制器類, ![](https://img2020.cnblogs.com/blog/587720/202010/587720-20201030170656999-2021419465.png) > new關鍵字顯式隱藏從基類繼承的成員 這樣我們既可以使用 Abp框架其他能力,利用new關鍵詞我們也刻意覆蓋了原有的 CurrentUser屬性, 其他同事也不需要額外的認知成本就可以開心地像往常一樣使用`CurrentUser`屬