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

下面给你演示一下如何添加一个功能项:

public class AppFeatureProvider : FeatureProvider { public override void SetFeatures(IFeatureDefinitionContext context) { var sampleBooleanFeature = context.Create("SampleBooleanFeature", defaultValue: "false"); sampleBooleanFeature.CreateChildFeature("SampleNumericFeature", defaultValue: "10"); context.Create("SampleSelectionFeature", defaultValue: "B"); } }

不用猜测 FeatureProvier 的实现了,他就是一个抽象类,定义了一个 SetFeatures 方法好让你实现而已。

之后我又在模块的预加载方法吧 AppFeatureProvider 添加到了IFeatureConfiguration 里面:

public class XXXModule : AbpModule { public override void PreInitialize() { Configuration.Features.Providers.Add<AppFeatureProvider>(); } }

而功能管理器则是在 Abp 核心模块 AbpKernalModule 初始化的时候,跟着权限管理器和系统设置管理器,一起被初始化了。

public override void PostInitialize() { RegisterMissingComponents(); // 这里是系统的设置的管理器 IocManager.Resolve<SettingDefinitionManager>().Initialize(); // 功能管理器在这里 IocManager.Resolve<FeatureManager>().Initialize(); // 权限管理器 IocManager.Resolve<PermissionManager>().Initialize(); IocManager.Resolve<LocalizationManager>().Initialize(); IocManager.Resolve<NotificationDefinitionManager>().Initialize(); IocManager.Resolve<NavigationManager>().Initialize(); if (Configuration.BackgroundJobs.IsJobExecutionEnabled) { var workerManager = IocManager.Resolve<IBackgroundWorkerManager>(); workerManager.Start(); workerManager.Add(IocManager.Resolve<IBackgroundJobManager>()); } }

看看功能管理器的定义就知道了:

public void Initialize() { foreach (var providerType in _featureConfiguration.Providers) { using (var provider = CreateProvider(providerType)) { provider.Object.SetFeatures(this); } } Features.AddAllFeatures(); }

波澜不惊的我早已看透一切,可以看到这里他通过遍历注入的 FeatureProvider 集合,传入自己,让他们可以向自己注入定义的功能项。

2.2.4 功能的存储

继续看 IFeatureChecker 的代码,最后从功能管理器拿到了功能项之后,就要根据租户的 Id 取得它具体的值了。值还能存在哪儿,除了数据库最合适放这种东西,其他的你愿意也可以存在 TXT 里面。

public interface IFeatureValueStore { // 很简洁,你传入当前用户的租户 Id 与 当前需要校验的功能项,我给你他的值 Task<string> GetValueOrNullAsync(int tenantId, Feature feature); }

废话不多说,来到 Zero 关于这个功能存储类的定义 AbpFeatureValueStore<TTenant,TUser>,你先不着急看那两个泛型参数,这两个泛型就是你的用户与租户实体,我们先看看这玩意儿继承了啥东西:

public class AbpFeatureValueStore<TTenant, TUser> : IAbpZeroFeatureValueStore, ITransientDependency, IEventHandler<EntityChangedEventData<Edition>>, IEventHandler<EntityChangedEventData<EditionFeatureSetting>> where TTenant : AbpTenant<TUser> where TUser : AbpUserBase

可以看到它首先继承了 IAbpZeroFeatureValueStore 接口,这里的 IAbpZeroFeatureValueStore 接口一样的继承的 IFeatureValueStore,所以在 Abp 底层框架能够直接使用。

其次我们还看到它监听了两个实体变更事件,也就是 Edition 与 EditFeatureSettings 表产生变化的时候,会进入到本类进行处理,其实这里的处理就是发生改变之后,拿到改变实体的 Id,从缓存清除掉脏数据而已。

然后我们直奔主题,找到方法的实现:

public virtual Task<string> GetValueOrNullAsync(int tenantId, Feature feature) { return GetValueOrNullAsync(tenantId, feature.Name); }

发现又是一个空壳子,继续跳转:

public virtual async Task<string> GetValueOrNullAsync(int tenantId, string featureName) { // 首先从租户功能值表获取功能的值 var cacheItem = await GetTenantFeatureCacheItemAsync(tenantId); // 获得到值 var value = cacheItem.FeatureValues.GetOrDefault(featureName); // 不等于空,优先获取租户的值而忽略掉版本的值 if (value != null) { return value; } // 如果租户功能值表的缓存说我还有版本 Id,那么就去版本级别的功能值表查找功能的值 if (cacheItem.EditionId.HasValue) { value = await GetEditionValueOrNullAsync(cacheItem.EditionId.Value, featureName); if (value != null) { return value; } } return null; }

这才是真正的获取功能值的地方,其余方法就不再详细讲述,这两个从缓存获取的方法,都分别有一个工厂方法从数据库拿去数据的,所以你也不用担心缓存里面不存在值的情况。

2.2.5 小结

总的来说功能是针对租户的一个权限,Abp 建议一个父母功能一般定义为 布尔功能。只有父母功能可用时,子功能才可用。ABP不强制这样做,但是建议这样做。

在一个基于 Abp 框架的系统功能权限是可选的,具体使用还是取决于你所开发的业务系统是否有这种需求。

2.3 权限(Permission) 2.3.1 权限的定义

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

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