[Abp vNext 源码分析] - 7. 权限与验证 (3)

在 ABP vNext 框架里面,所有用户定义的权限都是通过继承 PermissionDefinitionProvider,在其内部进行注册的。

public abstract class PermissionDefinitionProvider : IPermissionDefinitionProvider, ITransientDependency { public abstract void Define(IPermissionDefinitionContext context); }

开发人员继承了这个 Provider 之后,在 Define() 方法里面就可以注册自己的权限了,这里我以 Blog 模块的简化 Provider 为例。

public class BloggingPermissionDefinitionProvider : PermissionDefinitionProvider { public override void Define(IPermissionDefinitionContext context) { var bloggingGroup = context.AddGroup(BloggingPermissions.GroupName, L("Permission:Blogging")); // ... 其他代码。 var tags = bloggingGroup.AddPermission(BloggingPermissions.Tags.Default, L("Permission:Tags")); tags.AddChild(BloggingPermissions.Tags.Update, L("Permission:Edit")); tags.AddChild(BloggingPermissions.Tags.Delete, L("Permission:Delete")); tags.AddChild(BloggingPermissions.Tags.Create, L("Permission:Create")); var comments = bloggingGroup.AddPermission(BloggingPermissions.Comments.Default, L("Permission:Comments")); comments.AddChild(BloggingPermissions.Comments.Update, L("Permission:Edit")); comments.AddChild(BloggingPermissions.Comments.Delete, L("Permission:Delete")); comments.AddChild(BloggingPermissions.Comments.Create, L("Permission:Create")); } // 使用本地化字符串进行文本显示。 private static LocalizableString L(string name) { return LocalizableString.Create<BloggingResource>(name); } }

从上面的代码就可以看出来,权限被 ABP vNext 分成了 权限组定义权限定义,这两个东西我们后面进行重点讲述。那么这些 Provider 在什么时候被执行呢?找到权限模块的定义,可以看到如下代码:

[DependsOn( typeof(AbpSecurityModule), typeof(AbpLocalizationAbstractionsModule), typeof(AbpMultiTenancyModule) )] public class AbpAuthorizationModule : AbpModule { public override void PreConfigureServices(ServiceConfigurationContext context) { // 在 AutoFac 进行组件注册的时候,根据组件的类型定义视情况绑定拦截器。 context.Services.OnRegistred(AuthorizationInterceptorRegistrar.RegisterIfNeeded); // 在 AutoFac 进行组件注册的时候,根据组件的类型,判断是否是 Provider。 AutoAddDefinitionProviders(context.Services); } public override void ConfigureServices(ServiceConfigurationContext context) { // 注册认证授权服务。 context.Services.AddAuthorization(); // 替换掉 ASP.NET Core 提供的权限处理器,转而使用 ABP vNext 提供的权限处理器。 context.Services.AddSingleton<IAuthorizationHandler, PermissionRequirementHandler>(); // 这一部分是添加内置的一些权限值检查,后面我们在将 PermissionChecker 的时候会提到。 Configure<PermissionOptions>(options => { options.ValueProviders.Add<UserPermissionValueProvider>(); options.ValueProviders.Add<RolePermissionValueProvider>(); options.ValueProviders.Add<ClientPermissionValueProvider>(); }); } private static void AutoAddDefinitionProviders(IServiceCollection services) { var definitionProviders = new List<Type>(); services.OnRegistred(context => { if (typeof(IPermissionDefinitionProvider).IsAssignableFrom(context.ImplementationType)) { definitionProviders.Add(context.ImplementationType); } }); // 将获取到的 Provider 传递给 PermissionOptions 。 services.Configure<PermissionOptions>(options => { options.DefinitionProviders.AddIfNotContains(definitionProviders); }); } }

可以看到在注册组件的时候,ABP vNext 就会将这些 Provider 传递给 PermissionOptions ,我们根据 DefinitionProviders 字段找到有一个地方会使用到它,就是 PermissionDefinitionManager 类型的 CreatePermissionGroupDefinitions() 方法。

protected virtual Dictionary<string, PermissionGroupDefinition> CreatePermissionGroupDefinitions() { // 创建一个权限定义上下文。 var context = new PermissionDefinitionContext(); // 创建一个临时范围用于解析 Provider,Provider 解析完成之后即被释放。 using (var scope = _serviceProvider.CreateScope()) { // 根据之前的类型,通过 IoC 进行解析出实例,指定各个 Provider 的 Define() 方法,会向权限上下文填充权限。 var providers = Options .DefinitionProviders .Select(p => scope.ServiceProvider.GetRequiredService(p) as IPermissionDefinitionProvider) .ToList(); foreach (var provider in providers) { provider.Define(context); } } // 返回权限组名称 - 权限组定义的字典。 return context.Groups; }

你可能会奇怪,为什么返回的是一个权限组名字和定义的键值对,而不是返回的权限数据,我们之前添加的权限去哪儿了呢?

2.2.2 权限和权限组的定义

要搞清楚这个问题,我们首先要知道权限与权限组之间的关系是怎样的。回想我们之前在 Provider 里面添加权限的代码,首先我们是构建了一个权限组,然后往权限组里面添加的权限。权限组的作用就是将权限按照组的形式进行划分,方便代码进行访问于管理。

public class PermissionGroupDefinition { /// <summary> /// 唯一的权限组标识名称。 /// </summary> public string Name { get; } // 开发人员针对权限组的一些自定义属性。 public Dictionary<string, object> Properties { get; } // 权限所对应的本地化名称。 public ILocalizableString DisplayName { get => _displayName; set => _displayName = Check.NotNull(value, nameof(value)); } private ILocalizableString _displayName; /// <summary> /// 权限的适用范围,默认是租户/租主都适用。 /// 默认值: <see cref="MultiTenancySides.Both"/> /// </summary> public MultiTenancySides MultiTenancySide { get; set; } // 权限组下面的所属权限。 public IReadOnlyList<PermissionDefinition> Permissions => _permissions.ToImmutableList(); private readonly List<PermissionDefinition> _permissions; // 针对于自定义属性的快捷索引器。 public object this[string name] { get => Properties.GetOrDefault(name); set => Properties[name] = value; } protected internal PermissionGroupDefinition( string name, ILocalizableString displayName = null, MultiTenancySides multiTenancySide = MultiTenancySides.Both) { Name = name; // 没有传递多语言串,则使用权限组的唯一标识作为显示内容。 DisplayName = displayName ?? new FixedLocalizableString(Name); MultiTenancySide = multiTenancySide; Properties = new Dictionary<string, object>(); _permissions = new List<PermissionDefinition>(); } // 像权限组添加属于它的权限。 public virtual PermissionDefinition AddPermission( string name, ILocalizableString displayName = null, MultiTenancySides multiTenancySide = MultiTenancySides.Both) { var permission = new PermissionDefinition(name, displayName, multiTenancySide); _permissions.Add(permission); return permission; } // 递归构建权限集合,因为定义的某个权限内部还拥有子权限。 public virtual List<PermissionDefinition> GetPermissionsWithChildren() { var permissions = new List<PermissionDefinition>(); foreach (var permission in _permissions) { AddPermissionToListRecursively(permissions, permission); } return permissions; } // 递归构建方法。 private void AddPermissionToListRecursively(List<PermissionDefinition> permissions, PermissionDefinition permission) { permissions.Add(permission); foreach (var child in permission.Children) { AddPermissionToListRecursively(permissions, child); } } public override string ToString() { return $"[{nameof(PermissionGroupDefinition)} {Name}]"; } }

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

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