如何直接获取拥有指定权限的用户列表,无需全量查询后过滤?
直接检索拥有指定权限的用户列表
问题描述
当前代码通过全量查询所有用户后,逐个检查用户是否拥有指定权限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
相关产品推荐
相关产品推荐

