吐槽一下Abp的用户和租户管理模块

news/2025/2/22 1:53:54

1. 背景

ASP.NET Core 基于声明的访问控制到底是什么鬼?

聊到基于声明的身份认证将 身份和签发机构分离,应用程序信任签发机构,故认可签发的身份信息。

-----------
ClaimB站: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服务和控制器中是可以即时使用的。

------


2. Abp用户、租户管理

AbpICurrentUser获取不到常规HttpContext.User信息,是因为使用了特定的封装,封装的方式我不能苟同:

以下是 ICurrentUser 接口的基本属性:

IsAuthenticated 如果当前用户已登录(已认证),则返回 true. 如果用户尚未登录,则 Id 和 UserName 将返回 null.
Id (Guid?): 当前用户的Id,如果用户未登录,返回 null.
UserName (string): 当前用户的用户名称. 如果用户未登录,返回 null.
TenantId (Guid?): 当前用户的租户Id. 对于多租户 应用程序很有用. 如果当前用户未分配给租户,返回 null.
Email (string): 当前用户的电子邮件地址. 如果当前用户尚未登录或未设置电子邮件地址,返回 null.
Roles (string[]): 当前用户的角色. 返回当前用户角色名称的字符串数组.
.....

这里面有几个问题:

①    ICurrentUser将用户id、租户TenantId硬编码为GUID
项目原始的身份id、租户id若不为GUID,则根本不可用。
最差的情况也应该用个泛型,由应用决定特定身份片段的类型。

②      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;
        .....
    }

 ③  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,  在Abp CurrentUser中转换失败;Name也取值失败。  

在项目中就无法愉快地使用Abp ApplicationService、AbpController的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的通用服务类、控制器类:

new关键字显式隐藏从基类继承的成员

这样我们既可以使用 Abp框架其他能力,利用new关键词我们也刻意覆盖了框架原有的ICurrentUser属性,

其他同事也不需要额外的认知成本就可以开心地像往常一样使用CurrentUser属性。


  • 学完这篇依赖注入,与面试官扯皮就没有问题了。

  • 我又踩坑了!如何为HttpClient请求设置Content-Type标头?

  • 临近年关,修复ASP.NET Core因浏览器内核版本引发的单点登录故障

  • 手撕公司SSO登录原理

  • 实战解读ASP.NET Core身份认证

  • ASP.NET Core应用注意这一点,CTO会对你刮目相看

- END -

http://www.niftyadmin.cn/n/1864574.html

相关文章

在IIS中部署SPA应用,多么痛的领悟!

目前公司的Web项目是SPA应用,采用前后端分离开发,所以有时也会倒腾Vue框架。“前后端应用最终以容器形态、在k8s中部署, 为此我搭建了基于Gitlab flow的Devops流程。在Devops实践中,容器部署成为良方和事实标准。但是在开发和自测阶段&#x…

解锁环境变量在云原生应用中的各种姿势

应用程序在某些时刻总是需要一些外挂配置,云原生应用的实践是在容器化之前就将应用程序配置保留在代码之外。“12-Factors App:Store config in the environment① 外挂配置文件:业务配置 appsettings.json“可以在代码中要求加载appsetting.…

一套标准的ASP.NET Core容器化应用日志收集分析方案

点击上方蓝字给一个关注吧讲故事关注我公众号的朋友,应该知道我写了一些云原生应用收集和分析相关的文章,其中内容大多聚焦某个具体的组件:超级有用的TraceId,快点用起来吧!如何利用NLog输出结构化日志,并在…

Oh my God, Swagger API文档竟然可以这样写?

最好的总会在不经意间出现。“作为后端程序员,免不了与前端同事对接API, 一个书写良好的API设计文档可有效提高与前端对接的效率。为避免联调时来回撕逼,今天我们聊一聊正确编写Swaager API文档的姿势。基础Swagger用法在ConfigureServices配…

SQL常见基础疑点问答总结

以下文章都是摘自fcuandy 的,供自己和大家参考。 --建立测试环境IFobject_id(tb) ISNOTNULLDROPTABLEtb GOCREATETABLEtb(id INTIDENTITY(1,1),v VARCHAR(10)) GOINSERTtb SELECTaUNIONALLSELECTbINSERTtb SELECTxUNIONALLSELECTzGO(1)字串变量当数据库对象用 SQL c…

有关Quartz.NET,与一线码农大佬对个线?

跟[一线码农大佬]翻译的某技术文对个线最近看到一线码农大佬翻译的《如何在 ASP.NET Core 中使用 Quartz.NET 执行任务调度》,行文思路:安装Quartz.NETQuartz.NET 中的Job,triggers 和 Schedulers创建 Scheduler开启和停止 scheduler创建 job 工厂创建 J…

JS四舍五入

//保留小数点后两位function Transfer(num){var _value parseFloat(num);if(isNaN(_value)){return;}num _value.toFixed(2);return num;} 转载于:https://www.cnblogs.com/xjyggd/archive/2008/07/31/1257364.html

敏捷 | 如何正确理解敏捷?

【敏捷开发】| 作者/Edison Zhou在过去的五年时间里,我所在的公司和团队一直使用的都是敏捷开发模式,我也在2018年底获取了Scrum联盟的CSM认证,对于敏捷的理解也是从最初的感性认识到现在的理性认识。今天开始和你一起重新温习敏捷&#xff0…