abp PermissionDefinitionManager源码解析

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;
}
  • 作用:根据权限名称获取权限定义,若权限不存在则抛出异常(适用于 "必须存在该权限" 的场景);
  • 核心逻辑
    1. 调用 GetOrNullAsync 尝试获取权限(允许返回 null);
    2. 若返回 null,抛出 AbpException 异常,提示 "未定义的权限";
    3. 若存在,返回 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(安全获取,不抛异常);
  • 核心逻辑
    1. Check.NotNull:校验入参name不为 null,避免空指针(ABP 框架的参数校验规范);
    2. 优先从「静态权限存储」获取(_staticStore),若获取不到(返回 null),再从「动态权限存储」获取(_dynamicStore);
    3. ?? 是 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();
}
  • 核心作用:返回系统中所有有效的权限定义,保证 "静态权限不被动态权限覆盖";
  • 关键步骤解析
    1. ToImmutableHashSet():将静态权限名称转为不可变哈希集,保证查询性能(O (1) 时间复杂度);
    2. Concat:合并静态权限和动态权限,但通过Where过滤掉 "动态权限中与静态权限重名的项";
    3. 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(瞬态),因为类本身无状态,每次注入新实例可避免多线程问题,同时降低内存占用。

七、总结

  1. PermissionDefinitionManager 是 ABP 权限系统的 "元数据总管",统一管理静态 / 动态权限定义,是上层权限操作的基础;
  2. 核心规则:静态权限优先于动态权限,保证系统基础权限的稳定性;
  3. 设计特点:无状态、异步优先、不可变集合返回,符合 ABP 框架的最佳实践;
  4. 扩展点:可通过自定义IStaticPermissionDefinitionStore/IDynamicPermissionDefinitionStore,实现自定义的权限存储逻辑(如从配置文件 / 远程服务获取权限)。
相关推荐
叫我辉哥e119 小时前
### 技术文章大纲:C语言造轮子大赛
c语言·开发语言
奔跑的web.19 小时前
TypeScript 装饰器入门核心用法
前端·javascript·vue.js·typescript
阿蒙Amon19 小时前
TypeScript学习-第1章:入门
javascript·学习·typescript
winfredzhang19 小时前
实战复盘:如何用 HTML+JS+AI 打造一款“影迹”智能影视管理系统
javascript·html·json·加载·搜索·保存·电影接口
集成显卡19 小时前
Lucide Icons:一套现代、轻量且可定制的 SVG 图标库
前端·ui·图标库·lucide
guygg8820 小时前
NOMA功率分配与64 QAM调制中的SIC的MATLAB仿真
开发语言·matlab
pas13620 小时前
37-mini-vue 解析插值
前端·javascript·vue.js
flushmeteor20 小时前
JDK源码-基础类-String
java·开发语言
十里-21 小时前
vue.js 2前端开发的项目通过electron打包成exe
前端·vue.js·electron
u01092727121 小时前
C++中的策略模式变体
开发语言·c++·算法