Loading

造轮子之权限管理

上文已经完成了自定义授权策略,那么接下来就得完善我们的权限管理了。不然没有数据,如何鉴权~

表设计

创建我们的表实体类:

namespace Wheel.Domain.Permissions
{
    public class PermissionGrant : Entity<Guid>
    {
        public string Permission { get; set; }
        public string GrantType { get; set; }
        public string GrantValue { get; set; }
    }
}

Permission表示权限名称,结构为"{controllerName}:{actionName}"。
GrantType表示权限类型,如角色权限则R表示,方便后续在新增别的权限类型时可以灵活扩展。
GrantValue则表示权限类型对应的值,比如GrantType是R时,GrantValue是admin则表示admin角色的授权。

修改DbContext

在WheelDbContext添加代码:

#region Permission
public DbSet<PermissionGrant> PermissionGrants { get; set; }
#endregion
void ConfigurePermissionGrants(ModelBuilder builder)
{
    builder.Entity<PermissionGrant>(b =>
    {
        b.HasKey(o => o.Id);
        b.Property(o => o.Permission).HasMaxLength(128);
        b.Property(o => o.GrantValue).HasMaxLength(128);
        b.Property(o => o.GrantType).HasMaxLength(32);
    });
}

在OnModelCreating添加ConfigurePermissionGrants方法。

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);

    ConfigurePermissionGrants(builder);
}

接下来执行数据库迁移命令即可完成表创建。

实现权限管理

接下来就是实现我们的权限管理功能。
我们的PermissionManageAppService只需定义三个API即可满足管理需求。分别是获取当前用户所有权限,修改用户权限,获取指定角色权限。

namespace Wheel.Services.PermissionManage
{
    public interface IPermissionManageAppService : ITransientDependency
    {
        Task<R<List<GetAllPermissionDto>>> GetPermission();
        Task<R> UpdatePermission(UpdatePermissionDto dto);
        Task<R<List<GetAllPermissionDto>>> GetRolePermission(string RoleName);
    }
}

具体实现代码如下:

namespace Wheel.Services.PermissionManage
{
    public class PermissionManageAppService : WheelServiceBase, IPermissionManageAppService
    {
        private readonly IBasicRepository<PermissionGrant, Guid> _permissionGrantRepository;
        private readonly RoleManager<Role> _roleManager;
        private readonly XmlCommentHelper _xmlCommentHelper;
        public PermissionManageAppService(XmlCommentHelper xmlCommentHelper, IBasicRepository<PermissionGrant, Guid> permissionGrantRepository, RoleManager<Role> roleManager)
        {
            _xmlCommentHelper = xmlCommentHelper;
            _permissionGrantRepository = permissionGrantRepository;
            _roleManager = roleManager;
        }
        public async Task<R<List<GetAllPermissionDto>>> GetPermission()
        {
            var result = await GetAllDefinePermission();
            if (CurrentUser.IsInRoles("admin"))
            {
                result.ForEach(p => p.Permissions.ForEach(a => a.IsGranted = true));
            }
            else
            {
                var grantPermissions = (await _permissionGrantRepository
                    .SelectListAsync(a => a.GrantType == "R" && CurrentUser.Roles.Contains(a.GrantValue), a => a.Permission))
                    .Distinct().ToList();

                foreach (var group in result)
                {
                    foreach (var permission in group.Permissions)
                    {
                        if (grantPermissions.Any(b => b == $"{group.Group}:{permission.Name}"))
                            permission.IsGranted = true;
                        else
                            permission.IsGranted = false;
                    }
                }
            }
            return new R<List<GetAllPermissionDto>>(result);
        }
        public async Task<R<List<GetAllPermissionDto>>> GetRolePermission(string RoleName)
        {
            var result = await GetAllDefinePermission();

            var grantPermissions = (await _permissionGrantRepository
                .SelectListAsync(a => a.GrantType == "R" && RoleName == a.GrantValue, a => a.Permission))
                .Distinct().ToList();

            foreach (var group in result)
            {
                foreach (var permission in group.Permissions)
                {
                    if (grantPermissions.Any(b => b == $"{group.Group}:{permission.Name}"))
                        permission.IsGranted = true;
                    else
                        permission.IsGranted = false;
                }
            }

            return new R<List<GetAllPermissionDto>>(result);
        }
        public async Task<R> UpdatePermission(UpdatePermissionDto dto)
        {
            if(dto.Type == "R") 
            {
                var exsit = await _roleManager.RoleExistsAsync(dto.Value);
                if (!exsit)
                    throw new BusinessException(ErrorCode.RoleNotExist, "RoleNotExist")
                        .WithMessageDataData(dto.Value);
            }
            using (var tran = await UnitOfWork.BeginTransactionAsync())
            {
                await _permissionGrantRepository.DeleteAsync(a => a.GrantType == dto.Type && a.GrantValue == dto.Value);
                await _permissionGrantRepository.InsertManyAsync(dto.Permissions.Select(a=> new PermissionGrant 
                {
                    Id = GuidGenerator.Create(),
                    GrantType = dto.Type,
                    GrantValue = dto.Value,
                    Permission = a
                }).ToList());
                await DistributedCache.SetAsync($"Permission:{dto.Type}:{dto.Value}", dto.Permissions);
                await tran.CommitAsync();
            }
            return new R();
        }

        private ValueTask<List<GetAllPermissionDto>> GetAllDefinePermission()
        {

            var result = MemoryCache.Get<List<GetAllPermissionDto>>("AllDefinePermission");
            if (result == null)
            {
                result = new List<GetAllPermissionDto>();
                var apiDescriptionGroupCollectionProvider = ServiceProvider.GetRequiredService<IApiDescriptionGroupCollectionProvider>();
                var apiDescriptionGroups = apiDescriptionGroupCollectionProvider.ApiDescriptionGroups.Items.SelectMany(group => group.Items)
                    .Where(a => a.ActionDescriptor is ControllerActionDescriptor)
                    .GroupBy(a => (a.ActionDescriptor as ControllerActionDescriptor).ControllerTypeInfo);

                foreach (var apiDescriptions in apiDescriptionGroups)
                {
                    var permissionGroup = new GetAllPermissionDto();
                    var controllerTypeInfo = apiDescriptions.Key;

                    var controllerAllowAnonymous = controllerTypeInfo.GetAttribute<AllowAnonymousAttribute>();

                    var controllerComment = _xmlCommentHelper.GetTypeComment(controllerTypeInfo);

                    permissionGroup.Group = controllerTypeInfo.Name;
                    permissionGroup.Summary = controllerComment;
                    foreach (var apiDescription in apiDescriptions)
                    {
                        var method = controllerTypeInfo.GetMethod(apiDescription.ActionDescriptor.RouteValues["action"]);
                        var actionAllowAnonymous = method.GetAttribute<AllowAnonymousAttribute>();
                        var actionAuthorize = method.GetAttribute<AuthorizeAttribute>();
                        if ((controllerAllowAnonymous == null && actionAllowAnonymous == null) || actionAuthorize != null)
                        {
                            var methodComment = _xmlCommentHelper.GetMethodComment(method);
                            permissionGroup.Permissions.Add(new PermissionDto { Name = method.Name, Summary = methodComment });
                        }
                    }
                    if (permissionGroup.Permissions.Count > 0)
                        result.Add(permissionGroup);
                }
                MemoryCache.Set("AllDefinePermission", result);
            }
            return ValueTask.FromResult(result);
        }
    }
}

控制器代码如下:

namespace Wheel.Controllers
{
    /// <summary>
    /// 权限管理
    /// </summary>
    [Route("api/[controller]")]
    [ApiController]
    public class PermissionManageController : WheelControllerBase
    {
        private readonly IPermissionManageAppService _permissionManageAppService;
        public PermissionManageController(IPermissionManageAppService permissionManageAppService)
        {
            _permissionManageAppService = permissionManageAppService;
        }
        /// <summary>
        /// 获取所有权限
        /// </summary>
        /// <returns></returns>
        [HttpGet()]
        public Task<R<List<GetAllPermissionDto>>> GetPermission()
        {
            return _permissionManageAppService.GetPermission();
        }
        /// <summary>
        /// 获取指定角色权限
        /// </summary>
        /// <returns></returns>
        [HttpGet("{role}")]
        public Task<R<List<GetAllPermissionDto>>> GetRolePermission(string role)
        {
            return _permissionManageAppService.GetRolePermission(role);
        }
        /// <summary>
        /// 修改权限
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        [HttpPut]
        public async Task<R> UpdatePermission(UpdatePermissionDto dto)
        {
            return await _permissionManageAppService.UpdatePermission(dto);
        }
    }
}

通过读取XML注释文件,自动生成Controller和Action的注释名称。
将权限配置信息写入缓存,提供给PermissionChecker使用。
权限返回的结构如下:

namespace Wheel.Services.PermissionManage.Dtos
{
    public class GetAllPermissionDto
    {
        public string Group { get; set; } 
        public string Summary { get; set; } 

        public List<PermissionDto> Permissions { get; set; } = new ();
        
    }
    public class PermissionDto
    {
        public string Name { get; set; }
        public string Summary { get; set; }

        public bool IsGranted { get; set; }
    }
}

使用Postman测试API,可以看到,获取了我们的权限信息列表,按照Controller分组,细分到每一个Action,summary是我们XML注释的内容。
image.png

到这我们就完成了权限管理的API。

轮子仓库地址https://github.com/Wheel-Framework/Wheel
欢迎进群催更。

image.png

posted @ 2023-10-10 11:11  饭勺oO  阅读(820)  评论(0编辑  收藏  举报