• 造轮子之权限管理


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

    表设计#

    创建我们的表实体类:

    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 PermissionGrants { get; set; }
    #endregion
    
    void ConfigurePermissionGrants(ModelBuilder builder)
    {
        builder.Entity(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>> GetPermission();
            Task UpdatePermission(UpdatePermissionDto dto);
            Task>> GetRolePermission(string RoleName);
        }
    }
    

    具体实现代码如下:

    namespace Wheel.Services.PermissionManage
    {
        public class PermissionManageAppService : WheelServiceBase, IPermissionManageAppService
        {
            private readonly IBasicRepository _permissionGrantRepository;
            private readonly RoleManager _roleManager;
            private readonly XmlCommentHelper _xmlCommentHelper;
            public PermissionManageAppService(XmlCommentHelper xmlCommentHelper, IBasicRepository permissionGrantRepository, RoleManager roleManager)
            {
                _xmlCommentHelper = xmlCommentHelper;
                _permissionGrantRepository = permissionGrantRepository;
                _roleManager = roleManager;
            }
            public async Task>> 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>(result);
            }
            public async Task>> 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>(result);
            }
            public async Task 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> GetAllDefinePermission()
            {
    
                var result = MemoryCache.Get>("AllDefinePermission");
                if (result == null)
                {
                    result = new List();
                    var apiDescriptionGroupCollectionProvider = ServiceProvider.GetRequiredService();
                    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();
    
                        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();
                            var actionAuthorize = method.GetAttribute();
                            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
    {
        /// 
        /// 权限管理
        /// 
        [Route("api/[controller]")]
        [ApiController]
        public class PermissionManageController : WheelControllerBase
        {
            private readonly IPermissionManageAppService _permissionManageAppService;
            public PermissionManageController(IPermissionManageAppService permissionManageAppService)
            {
                _permissionManageAppService = permissionManageAppService;
            }
            /// 
            /// 获取所有权限
            /// 
            /// 
            [HttpGet()]
            public Task>> GetPermission()
            {
                return _permissionManageAppService.GetPermission();
            }
            /// 
            /// 获取指定角色权限
            /// 
            /// 
            [HttpGet("{role}")]
            public Task>> GetRolePermission(string role)
            {
                return _permissionManageAppService.GetRolePermission(role);
            }
            /// 
            /// 修改权限
            /// 
            /// 
            /// 
            [HttpPut]
            public async Task 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 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

  • 相关阅读:
    Hashtable 相关面试集合(2022)
    多线程和并发编程(3)—AQS和ReentrantLock实现的互斥锁
    git常用命令总结
    谁在滋养你,谁在消耗你
    一份详实的 Scrapy 爬虫教程,从原理到实战
    数据集笔记:GeoLife GPS 数据 (user guide)
    计算机毕业设计Java智能云税导引服务机器人云端管理(源码+系统+mysql数据库+lw文档)
    Vue+element 商品列表、新增、编辑、删除业务实现
    00后最关注程序员,超8成人接受灵活就业,视频UP主是最想从事的职业
    莫烦 Python 基础
  • 原文地址:https://www.cnblogs.com/fanshaoO/p/17754156.html