cs
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.DependencyInjection;
namespace Volo.Abp.Authorization.Permissions;
public class PermissionDefinitionManager : IPermissionDefinitionManager, ITransientDependency
{
private readonly IStaticPermissionDefinitionStore _staticStore;
private readonly IDynamicPermissionDefinitionStore _dynamicStore;
public PermissionDefinitionManager(
IStaticPermissionDefinitionStore staticStore,
IDynamicPermissionDefinitionStore dynamicStore)
{
_staticStore = staticStore;
_dynamicStore = dynamicStore;
}
public virtual async Task<PermissionDefinition> GetAsync(string name)
{
var permission = await GetOrNullAsync(name);
if (permission == null)
{
throw new AbpException("Undefined permission: " + name);
}
return permission;
}
public virtual async Task<PermissionDefinition?> GetOrNullAsync(string name)
{
Check.NotNull(name, nameof(name));
return await _staticStore.GetOrNullAsync(name) ??
await _dynamicStore.GetOrNullAsync(name);
}
public virtual async Task<PermissionDefinition> GetResourcePermissionAsync(string resourceName, string name)
{
var permission = await GetResourcePermissionOrNullAsync(resourceName, name);
if (permission == null)
{
throw new AbpException($"Undefined resource permission: {name} for resource: {resourceName}");
}
return permission;
}
public virtual async Task<PermissionDefinition?> GetResourcePermissionOrNullAsync(string resourceName, string name)
{
Check.NotNull(name, nameof(name));
return await _staticStore.GetResourcePermissionOrNullAsync(resourceName, name) ??
await _dynamicStore.GetResourcePermissionOrNullAsync(resourceName, name);
}
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
var staticPermissions = await _staticStore.GetPermissionsAsync();
var staticPermissionNames = staticPermissions
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicPermissions = await _dynamicStore.GetPermissionsAsync();
/* We prefer static permissions over dynamics */
return staticPermissions.Concat(
dynamicPermissions.Where(d => !staticPermissionNames.Contains(d.Name))
).ToImmutableList();
}
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetResourcePermissionsAsync()
{
var staticResourcePermissions = await _staticStore.GetResourcePermissionsAsync();
var staticResourcePermissionNames = staticResourcePermissions
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicResourcePermissions = await _dynamicStore.GetResourcePermissionsAsync();
/* We prefer static permissions over dynamics */
return staticResourcePermissions.Concat(
dynamicResourcePermissions.Where(d => !staticResourcePermissionNames.Contains(d.Name))
).ToImmutableList();
}
public virtual async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
var staticGroups = await _staticStore.GetGroupsAsync();
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicGroups = await _dynamicStore.GetGroupsAsync();
/* We prefer static groups over dynamics */
return staticGroups.Concat(
dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name))
).ToImmutableList();
}
}
一、类核心概述
PermissionDefinitionManager 是 ABP 框架权限系统的核心管理类,负责统一管理「静态定义」和「动态定义」的权限元数据(权限、权限组、资源权限),是上层权限检查、权限展示(如 Admin UI)的基础数据入口。
核心定位:
- 作为
IPermissionDefinitionManager接口的默认实现,封装对「静态权限存储」和「动态权限存储」的统一访问; - 遵循「静态权限优先于动态权限」的规则,保证权限定义的稳定性(静态权限通常是代码中固定定义的,动态权限是运行时 / 数据库配置的);
- 所有方法均为异步设计,适配 ABP 框架的异步编程规范。
二、基础信息
| 项 | 具体内容 |
|---|---|
| 命名空间 | Volo.Abp.Authorization.Permissions |
| 实现接口 | IPermissionDefinitionManager(权限定义管理核心接口)、ITransientDependency(瞬态依赖注入) |
| 依赖注入生命周期 | 瞬态(每次请求 / 注入都创建新实例,无状态设计) |
| 核心依赖组件 | - IStaticPermissionDefinitionStore:静态权限存储(代码定义的权限,如PermissionDefinitionProvider)- IDynamicPermissionDefinitionStore:动态权限存储(运行时 / 数据库配置的权限) |
三、构造函数解析
cs
public PermissionDefinitionManager(
IStaticPermissionDefinitionStore staticStore,
IDynamicPermissionDefinitionStore dynamicStore)
{
_staticStore = staticStore;
_dynamicStore = dynamicStore;
}
- 核心作用:通过依赖注入获取「静态权限存储」和「动态权限存储」的实例,为后续统一访问做准备;
- 设计原则:依赖抽象而非具体实现,符合依赖注入的 "面向接口编程" 最佳实践,便于扩展自定义的权限存储。
四、核心方法逐行解析
1. GetAsync(string name):获取指定名称的权限定义(必存在)
cs
public virtual async Task<PermissionDefinition> GetAsync(string name)
{
var permission = await GetOrNullAsync(name);
if (permission == null)
{
throw new AbpException("Undefined permission: " + name);
}
return permission;
}
- 作用:根据权限名称获取权限定义,若权限不存在则抛出异常(适用于 "必须存在该权限" 的场景);
- 核心逻辑 :
- 调用
GetOrNullAsync尝试获取权限(允许返回 null); - 若返回 null,抛出
AbpException异常,提示 "未定义的权限"; - 若存在,返回
PermissionDefinition(权限定义实体,包含名称、显示名、是否启用等核心属性);
- 调用
- 使用场景:业务代码中明确依赖某个权限存在时(如权限检查前验证权限是否定义)。
2. GetOrNullAsync(string name):获取指定名称的权限定义(允许不存在)
cs
public virtual async Task<PermissionDefinition?> GetOrNullAsync(string name)
{
Check.NotNull(name, nameof(name));
return await _staticStore.GetOrNullAsync(name) ??
await _dynamicStore.GetOrNullAsync(name);
}
- 作用:根据权限名称获取权限定义,不存在则返回 null(安全获取,不抛异常);
- 核心逻辑 :
Check.NotNull:校验入参name不为 null,避免空指针(ABP 框架的参数校验规范);- 优先从「静态权限存储」获取(
_staticStore),若获取不到(返回 null),再从「动态权限存储」获取(_dynamicStore); ??是 C# 空合并运算符,实现 "静态优先" 的核心规则;
- 关键设计:静态权限优先级高于动态权限,保证代码中定义的基础权限不被动态配置覆盖。
3. GetResourcePermissionAsync(string resourceName, string name):获取指定资源的权限定义(必存在)
csharp
运行
cs
public virtual async Task<PermissionDefinition> GetResourcePermissionAsync(string resourceName, string name)
{
var permission = await GetResourcePermissionOrNullAsync(resourceName, name);
if (permission == null)
{
throw new AbpException($"Undefined resource permission: {name} for resource: {resourceName}");
}
return permission;
}
- 作用:获取指定资源(如 "用户资源""角色资源")下的指定权限定义,不存在则抛异常;
- 场景适配:ABP 支持 "资源级权限"(如不同业务资源下的同名权限),该方法用于精准定位资源下的权限;
- 逻辑复用 :内部调用
GetResourcePermissionOrNullAsync,仅增加异常抛出逻辑(和GetAsync设计一致)。
4. GetResourcePermissionOrNullAsync(string resourceName, string name):获取指定资源的权限定义(允许不存在)
csharp
运行
cs
public virtual async Task<PermissionDefinition?> GetResourcePermissionOrNullAsync(string resourceName, string name)
{
Check.NotNull(name, nameof(name));
return await _staticStore.GetResourcePermissionOrNullAsync(resourceName, name) ??
await _dynamicStore.GetResourcePermissionOrNullAsync(resourceName, name);
}
- 核心逻辑 :和
GetOrNullAsync完全一致,仅扩展了 "资源名称" 参数,优先从静态存储获取资源权限,再从动态存储获取; - 参数说明 :
resourceName是资源唯一标识(如Identity.User),name是资源下的权限名称(如Create)。
5. GetPermissionsAsync():获取所有权限定义(静态 + 动态,去重)
csharp
运行
cs
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetPermissionsAsync()
{
// 1. 获取所有静态权限
var staticPermissions = await _staticStore.GetPermissionsAsync();
// 2. 提取静态权限名称,存入不可变哈希集(高效去重)
var staticPermissionNames = staticPermissions
.Select(p => p.Name)
.ToImmutableHashSet();
// 3. 获取所有动态权限
var dynamicPermissions = await _dynamicStore.GetPermissionsAsync();
/* We prefer static permissions over dynamics */
// 4. 合并:静态权限 + 未在静态中定义的动态权限
return staticPermissions.Concat(
dynamicPermissions.Where(d => !staticPermissionNames.Contains(d.Name))
).ToImmutableList();
}
- 核心作用:返回系统中所有有效的权限定义,保证 "静态权限不被动态权限覆盖";
- 关键步骤解析 :
ToImmutableHashSet():将静态权限名称转为不可变哈希集,保证查询性能(O (1) 时间复杂度);Concat:合并静态权限和动态权限,但通过Where过滤掉 "动态权限中与静态权限重名的项";ToImmutableList():返回只读不可变列表,避免外部修改权限定义(保证数据安全性);
- 设计目的:静态权限是系统基础权限,不允许动态权限覆盖,仅补充静态中没有的动态权限。
6. GetResourcePermissionsAsync():获取所有资源权限定义(静态 + 动态,去重)
csharp
运行
cs
public virtual async Task<IReadOnlyList<PermissionDefinition>> GetResourcePermissionsAsync()
{
var staticResourcePermissions = await _staticStore.GetResourcePermissionsAsync();
var staticResourcePermissionNames = staticResourcePermissions
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicResourcePermissions = await _dynamicStore.GetResourcePermissionsAsync();
/* We prefer static permissions over dynamics */
return staticResourcePermissions.Concat(
dynamicResourcePermissions.Where(d => !staticResourcePermissionNames.Contains(d.Name))
).ToImmutableList();
}
- 核心逻辑 :和
GetPermissionsAsync完全一致,仅操作对象从 "所有权限" 变为 "资源权限"; - 场景适配:专门用于管理 "资源级权限" 的元数据,适配 ABP 的资源权限隔离设计。
7. GetGroupsAsync():获取所有权限组定义(静态 + 动态,去重)
csharp
运行
cs
public virtual async Task<IReadOnlyList<PermissionGroupDefinition>> GetGroupsAsync()
{
var staticGroups = await _staticStore.GetGroupsAsync();
var staticGroupNames = staticGroups
.Select(p => p.Name)
.ToImmutableHashSet();
var dynamicGroups = await _dynamicStore.GetGroupsAsync();
/* We prefer static groups over dynamics */
return staticGroups.Concat(
dynamicGroups.Where(d => !staticGroupNames.Contains(d.Name))
).ToImmutableList();
}
- 核心作用:返回系统中所有权限组定义(权限组用于分类管理权限,如 "用户管理组""角色管理组");
- 设计规则:和权限定义一致,静态权限组优先于动态权限组,避免动态配置覆盖基础分组结构。
五、核心设计思路
1. 静态优先原则
所有方法均遵循「静态权限 / 组 > 动态权限 / 组」的规则,保证系统基础权限的稳定性:
- 静态权限:通过
PermissionDefinitionProvider在代码中定义,是系统运行的基础; - 动态权限:通过数据库 / 配置中心动态配置,仅作为补充,无法覆盖静态权限。
2. 无状态设计
类本身不存储任何权限数据,仅作为 "静态存储" 和 "动态存储" 的统一访问入口,所有数据均由依赖的存储组件提供,便于扩展和测试。
3. 不可变集合
所有返回值均为IReadOnlyList/ImmutableList/ImmutableHashSet:
- 避免外部代码修改权限元数据,保证数据一致性;
- 不可变集合是线程安全的,适配多线程场景。
4. 异步优先
所有核心方法均为async/await异步设计,适配 ABP 框架的异步编程模型,避免阻塞线程(尤其是动态存储可能涉及数据库 / 远程调用)。
六、关键细节与避坑点
1. 参数校验
所有方法的入参(如name)都会通过Check.NotNull校验,避免空指针异常,这是 ABP 框架的通用参数校验规范。
2. 异常抛出规则
- 以
Get开头的方法(如GetAsync):权限不存在时抛AbpException,适用于 "必须存在该权限" 的场景; - 以
GetOrNull开头的方法(如GetOrNullAsync):权限不存在时返回 null,适用于 "安全检查" 场景。
3. 依赖注入生命周期
类标记为ITransientDependency(瞬态),因为类本身无状态,每次注入新实例可避免多线程问题,同时降低内存占用。
七、总结
PermissionDefinitionManager是 ABP 权限系统的 "元数据总管",统一管理静态 / 动态权限定义,是上层权限操作的基础;- 核心规则:静态权限优先于动态权限,保证系统基础权限的稳定性;
- 设计特点:无状态、异步优先、不可变集合返回,符合 ABP 框架的最佳实践;
- 扩展点:可通过自定义
IStaticPermissionDefinitionStore/IDynamicPermissionDefinitionStore,实现自定义的权限存储逻辑(如从配置文件 / 远程服务获取权限)。