[Abp 源码分析]十二、多租户体系与权限验证

承接上篇文章我们会在这篇文章详细解说一下 Abp 是如何结合 IPermissionChecker 与 IFeatureChecker 来实现一个完整的多租户系统的权限校验的。

1.多租户的概念

多租户系统又被称之为 Saas ,比如阿里云就是一个典型的多租户系统,用户本身就是一个租户,可以在上面购买自己的 ECS 实例,并且自己的数据与其他使用者(租户)所隔绝,两者的数据都是不可见的。

那么 Abp 是如何实现数据隔离的呢?

1.1 单部署-单数据库

如果你的软件系统仅部署一个实例,并且所有租户的数据都是存放在一个数据库里面的,那么可以通过一个 TenantId (租户 Id) 来进行数据隔离。那么当我们执行 SELECT 操作的时候就会附加上当前登录用户租户 Id 作为过滤条件,那么查出来的数据也仅仅是当前租户的数据,而不会查询到其他租户的数据。

[Abp 源码分析]十二、多租户体系与权限验证

1.2 单部署-多数据库

Abp 还提供了另外一种方式,即为每一个租户提供一个单独的数据库,在用户登录的时候根据用户对应的租户 ID,从一个数据库连接映射表获取到当前租户对应的数据库连接字符串,并且在查询数据与写入数据的时候,不同租户操作的数据库是不一样的。

[Abp 源码分析]十二、多租户体系与权限验证

2.多租户系统的权限验证

从上一篇文章我们知道了在权限过滤器与权限拦截器当中,最终会使用 IFeatureChecker 与 IPermissionChecker 来进行权限校验,并且它还持久一个用户会话状态 IAbpSession 用于存储识别当前访问网站的用户是谁。

2.1 用户会话状态

基本做过网站程序开发的同学都知道用于区分每一个用户,我们需要通过 Session 来保存当前用户的状态,以便进行权限验证或者其他操作。而 Abp 框架则为我们定义了一个统一的会话状态接口 IAbpSession ,用于标识当前用户的状态。在其接口当中主要定义了三个重要的属性,第一个 UserId (用户 Id),第二个就是 TenantId (租户 Id),以及用于确定当前用户是租户还是租主的 MultiTenancySides 属性。

除此之外,还拥有一个 Use() 方法,用户在某些时候临时替换掉当前用户的 UserId 与 TenantId 的值,这个方法在我的 《Abp + Grpc 如何实现用户会话状态传递》 文章当中有讲到过。

而针对这个方法的实现又可以扯出一大堆知识,这块我们放在后面再进行精讲,这里我们还是主要通篇讲解一下多租户体系下的数据过滤与权限验证。

[Abp 源码分析]十二、多租户体系与权限验证

2.1.1 默认会话状态的实现

IAbpSession 当中的值默认是从 JWT 当中取得的,这取决于它的默认实现 ClaimsAbpSession,它还继承了一个抽象父类 AbpSessionBase ,这个父类主要是实现了 Use() 方法,这里略过。

在其默认实现里面,重载了 UserId 与 TenantId 的获取方法。

public override long? UserId { get { // ... 其他代码 var userIdClaim = PrincipalAccessor.Principal?.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.UserId); // ... 其他代码 long userId; if (!long.TryParse(userIdClaim.Value, out userId)) return null; return userId; } }

可以看到这里是通过 PrincipalAccessor 从当前请求的请求头中获取 Token ,并从 Claims 里面获取 Type 值为 AbpClaimTypes.UserId 的对象,将其转换为 long 类型的 UserId,这样就拿到了当前用户登录的 Id 了。

2.1.2 获取当前请求的用户状态

这里的 PrincipalAccessor 是一个 IPrincipalAccessor 接口,在 ASP .NET Core 库当中他的实现名字叫做 AspNetCorePrincipalAccessor。其实你应该猜得到,在这个类的构造函数当中,注入了 HttpContext 的访问器对象 IHttpContextAccessor,这样 IAbpSession 就可以轻而易举地获得当前请求上下文当中的具体数据了。

public class AspNetCorePrincipalAccessor : DefaultPrincipalAccessor { public override ClaimsPrincipal Principal => _httpContextAccessor.HttpContext?.User ?? base.Principal; private readonly IHttpContextAccessor _httpContextAccessor; public AspNetCorePrincipalAccessor(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } } 2.1.3 小结

所以,Abp 通过 IAbpSession 可以轻松地知道我们当前用户的状态,包括用户 Id 与租户 Id,它只需要知道这两个东西,就可以很简单的在 IFeatureChecker 和 IPermissionChecker 当中来查询用户所绑定的权限来进行验证。

2.2 功能(Feature)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxzpd.html