You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何直接获取拥有指定权限的用户列表,无需全量查询后过滤?

直接检索拥有指定权限的用户列表

问题描述

当前代码通过全量查询所有用户后,逐个检查用户是否拥有指定权限Module.MyModule.Assignable,这种方式在用户量较大时性能低下,希望优化为直接检索符合权限要求的用户,避免全量查询。

原实现代码:

public async Task<List<PermissionedUserSummaryDto>> GetAssignableUsersAsync()
{
    var users = await _userRepository.GetListAsync();
    var result = new List<PermissionedUserSummaryDto>();
    var permissionName = "Module.MyModule.Assignable";

    foreach (var user in users)
    {
        if (await IsGrantedAsync(user.Id, permissionName))
        {
            result.Add(new PermissionedUserSummaryDto
            {
                Id = user.Id,
                UserName = user.UserName,
            });
        }
    }

    return result;
}

private async Task<bool> IsGrantedAsync(Guid userId, string permissionName)
{
    var claimsPrincipal = await GetClaimsPrincipalForUserAsync(userId);
    return await _permissionChecker.IsGrantedAsync(claimsPrincipal, permissionName);
}

private async Task<ClaimsPrincipal> GetClaimsPrincipalForUserAsync(Guid userId)
{
    var user = await _userRepository.GetAsync(userId);
    if (user == null)
    {
        throw new InvalidOperationException($"User with ID {userId} not found.");
    }

    var claims = new[]
    {
        new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
        new Claim(ClaimTypes.Name, user.UserName)
    };

    var identity = new ClaimsIdentity(claims, "Custom");
    var claimsPrincipal = new ClaimsPrincipal(identity);

    return claimsPrincipal;
}

优化方案

根据权限的存储方式,提供以下几种直接检索的实现方式:

1. 基于角色关联权限(用户→角色→权限)

如果权限是通过角色间接授予用户,可通过多表关联查询直接筛选出拥有目标权限的用户:

public async Task<List<PermissionedUserSummaryDto>> GetAssignableUsersAsync()
{
    var permissionName = "Module.MyModule.Assignable";

    var query = from user in _userRepository.Query
                join userRole in _userRoleRepository.Query on user.Id equals userRole.UserId
                join rolePermission in _rolePermissionRepository.Query on userRole.RoleId equals rolePermission.RoleId
                where rolePermission.PermissionName == permissionName
                select new PermissionedUserSummaryDto
                {
                    Id = user.Id,
                    UserName = user.UserName
                };

    // 去重处理,避免同一用户属于多个有权限角色时重复返回
    return await query.Distinct().ToListAsync();
}

2. 支持用户直接绑定权限(用户→权限 + 用户→角色→权限)

如果系统允许直接给用户分配权限,同时保留角色权限机制,可合并两种来源的查询结果:

public async Task<List<PermissionedUserSummaryDto>> GetAssignableUsersAsync()
{
    var permissionName = "Module.MyModule.Assignable";

    // 查询直接拥有该权限的用户
    var directUserQuery = from user in _userRepository.Query
                          join userPermission in _userPermissionRepository.Query on user.Id equals userPermission.UserId
                          where userPermission.PermissionName == permissionName
                          select new PermissionedUserSummaryDto
                          {
                              Id = user.Id,
                              UserName = user.UserName
                          };

    // 查询通过角色间接拥有该权限的用户
    var roleBasedUserQuery = from user in _userRepository.Query
                             join userRole in _userRoleRepository.Query on user.Id equals userRole.UserId
                             join rolePermission in _rolePermissionRepository.Query on userRole.RoleId equals rolePermission.RoleId
                             where rolePermission.PermissionName == permissionName
                             select new PermissionedUserSummaryDto
                             {
                                 Id = user.Id,
                                 UserName = user.UserName
                             };

    // 合并结果并去重
    var combinedQuery = directUserQuery.Union(roleBasedUserQuery);
    return await combinedQuery.Distinct().ToListAsync();
}

3. 基于ABP框架的权限存储

如果使用ABP框架,可直接查询框架内置的PermissionGrant表来获取有权限的用户:

public async Task<List<PermissionedUserSummaryDto>> GetAssignableUsersAsync()
{
    var permissionName = "Module.MyModule.Assignable";

    // 获取所有授予该权限的记录(用户直接授予/角色授予)
    var permissionGrants = await _permissionGrantRepository.GetListAsync(g => g.Name == permissionName);

    // 提取直接授权的用户ID
    var directUserIds = permissionGrants
        .Where(g => g.ProviderName == "User")
        .Select(g => Guid.Parse(g.ProviderKey))
        .ToList();

    // 提取角色授权对应的角色ID,再关联获取这些角色下的用户ID
    var roleIds = permissionGrants
        .Where(g => g.ProviderName == "Role")
        .Select(g => g.ProviderKey)
        .ToList();
    var roleUserIds = await _userRoleRepository
        .GetListAsync(ur => roleIds.Contains(ur.RoleId))
        .Select(ur => ur.UserId)
        .ToList();

    // 合并所有有权限的用户ID并去重
    var allUserIds = directUserIds.Union(roleUserIds).Distinct();

    // 查询这些用户的基础信息
    var targetUsers = await _userRepository.GetListAsync(u => allUserIds.Contains(u.Id));
    return targetUsers.Select(u => new PermissionedUserSummaryDto
    {
        Id = u.Id,
        UserName = u.UserName
    }).ToList();
}

核心优化点

  • 避免全量查询用户,通过数据库层面的关联筛选直接获取目标用户,减少数据传输量
  • 减少循环中的异步权限检查操作,避免多次数据库查询带来的性能损耗
  • 利用数据库的查询优化能力(如索引)提升检索效率

内容的提问来源于stack exchange,提问作者user29962100

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:14:50