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

首先我们的思绪回到上一章所讲的 AuthorizationHelper 类,在其 AuthorizeAsync() 方法当中,使用 IFeatureChecker 来检测用户是否拥有某种功能。

public virtual async Task AuthorizeAsync(MethodInfo methodInfo, Type type) { // 检测功能 await CheckFeatures(methodInfo, type); // 检测权限 await CheckPermissions(methodInfo, type); }

然后呢,在 IFeatureChecker.CheckFeatures() 方法的内部,跟 IPermissionChecker 的套路一样,这里仍然是一个扩展方法,遍历方法/类上标记的 [RequiresFeatureAttribute] 特性,调用 IFeatureChecker 的 GetValueAsync() 方法传入功能的名称,然后将其值与 "true" 相比较,为真则是启用了该功能,其他值则说明没有启用。

public static async Task<bool> IsEnabledAsync(this IFeatureChecker featureChecker, string featureName) { // 检查是否启用 return string.Equals(await featureChecker.GetValueAsync(featureName), "true", StringComparison.OrdinalIgnoreCase); }

IFeatureChecker 的定义:

public interface IFeatureChecker { // 传入功能名字,获取真这对于当前租户其默认值 Task<string> GetValueAsync(string name); // 传入租户 Id 与功能名字,获取针对于指定 Id 租户的默认值 Task<string> GetValueAsync(int tenantId, string name); }

到这一步我们仍然是跟 IFeatureChecker 打交道,那么他的具体实现是怎样的呢?

先来看一下这个 IFeatureChecker 的依赖关系图:

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

目前看起来还是比较简单,他拥有一个默认实现 FeatureChecker ,其中 IFeatureValueStore 从名字就可以知道它是用来存储功能列表的,而 IFeatureManager 则是用来管理这些功能的,Feature 则是这些功能的定义。

结合之前在 IsEnabledAsync() 方法的调用,可以看到它先进入的 GetValueAsync(string name) 方法,判断当前用户的租户 Id 是否有值,如果没有值则直接抛出异常,中断权限验证。如果有值得话,传入当前登录用户的租户 Id ,从 IFeatureManager 当中获取到定义的权限,之后呢从 IFeatureValueStore 当中拿到功能具体的值,因为功能是针对租户而言的,所以一个功能针对于多个租户的值肯定是不同的,所以在这里查询具体值的时候需要传入租户 Id。

public class FeatureChecker : IFeatureChecker, ITransientDependency { public IAbpSession AbpSession { get; set; } public IFeatureValueStore FeatureValueStore { get; set; } private readonly IFeatureManager _featureManager; public FeatureChecker(IFeatureManager featureManager) { _featureManager = featureManager; FeatureValueStore = NullFeatureValueStore.Instance; AbpSession = NullAbpSession.Instance; } public Task<string> GetValueAsync(string name) { // 判断当前登录的用户是否拥有租户 ID if (!AbpSession.TenantId.HasValue) { throw new AbpException("FeatureChecker can not get a feature value by name. TenantId is not set in the IAbpSession!"); } // 传入当前登录用户的租户 Id ,获取其值 return GetValueAsync(AbpSession.TenantId.Value, name); } public async Task<string> GetValueAsync(int tenantId, string name) { // 从功能管理器根据名字查询用户定义的功能 var feature = _featureManager.Get(name); // 获得功能的值,如果没有值则返回其默认值 var value = await FeatureValueStore.GetValueOrNullAsync(tenantId, feature); if (value == null) { return feature.DefaultValue; } return value; } }

聪明的你肯定猜到功能其实是用户在代码当中定义的,而功能的值则是存放在数据库当中,每个租户其值都是不一样的。这是不是让你想到了系列文章 《[Abp 源码分析]五、系统设置》 SettingProvider 的实现呢?

So,这里的 IFeatureStore 的默认实现肯定是从数据库进行配置咯~

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

2.2.1 功能的定义

首先功能、权限都是树形结构,他们都可以拥有自己的子节点,这样可以直接实现针对父节点赋值而拥有其子节点的所有权限。这里先来看一下功能的的基本定义:

public class Feature { // 附加数据的一个索引器 public object this[string key] { get => Attributes.GetOrDefault(key); set => Attributes[key] = value; } // 功能的附加数据 public IDictionary<string, object> Attributes { get; private set; } // 父级功能 public Feature Parent { get; private set; } // 功能的名称 public string Name { get; private set; } // 功能的展示名称,这是一个本地化字符串 public ILocalizableString DisplayName { get; set; } // 功能的描述,一样的是一个本地化字符串 public ILocalizableString Description { get; set; } // 功能的输入类型 public IInputType InputType { get; set; } // 功能的默认值 public string DefaultValue { get; set; } // 功能所适用的范围 public FeatureScopes Scope { get; set; } // 如果当前功能的子节点的不可变集合 public IReadOnlyList<Feature> Children => _children.ToImmutableList(); private readonly List<Feature> _children; public Feature(string name, string defaultValue, ILocalizableString displayName = null, ILocalizableString description = null, FeatureScopes scope = FeatureScopes.All, IInputType inputType = null) { Name = name ?? throw new ArgumentNullException("name"); DisplayName = displayName; Description = description; Scope = scope; DefaultValue = defaultValue; InputType = inputType ?? new CheckboxInputType(); _children = new List<Feature>(); Attributes = new Dictionary<string, object>(); } public Feature CreateChildFeature(string name, string defaultValue, ILocalizableString displayName = null, ILocalizableString description = null, FeatureScopes scope = FeatureScopes.All, IInputType inputType = null) { var feature = new Feature(name, defaultValue, displayName, description, scope, inputType) { Parent = this }; _children.Add(feature); return feature; } public override string ToString() { return string.Format("[Feature: {0}]", Name); } }

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

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