造轮子之权限管理

轮子,权限,管理 · 浏览次数 : 58

小编点评

该代码介绍了权限管理的API,轮子仓库地址https://github.com/Wheel-Framework/Wheel。 主要内容如下: 1. **权限管理控制器 PermissionManageController:** - 负责获取所有权限、获取指定角色权限、修改权限。 - 使用IPermissionManageAppService获取权限信息。 - 提供给PermissionChecker使用。 2. **GetPermission()方法:** - 获取所有权限信息。 - 使用IPermissionManageAppService获取权限信息。 3. **GetRolePermission(string role)方法:** - 获取指定角色权限信息。 - 使用IPermissionManageAppService获取权限信息。 4. **UpdatePermission(UpdatePermissionDto dto)方法:** - 修改权限信息。 - 使用IPermissionManageAppService更新权限信息。 5. **Postman测试API:** - 可以测试获取所有权限信息列表,按照Controller分组,细分到每一个Action,summary是XML注释的内容。 6. **其他方法:** - 提供一些其他方法,例如获取指定角色权限信息、修改权限等。 代码清晰易懂,展示了权限管理API的结构和功能。

正文

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

表设计

创建我们的表实体类:

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

与造轮子之权限管理相似的内容:

造轮子之权限管理

上文已经完成了自定义授权策略,那么接下来就得完善我们的权限管理了。不然没有数据,如何鉴权~ 表设计 创建我们的表实体类: namespace Wheel.Domain.Permissions { public class PermissionGrant : Entity { public

造轮子之菜单管理

前面完成了基础管理的相关API,接下来就得做一个菜单管理了,用于对接管理后台前端界面。 设计菜单结构 菜单是一个多级结构,所以我们得设计一个树形的。包含自己上级和下级的属性。同时预留Permission用于做可选的权限限制。 namespace Wheel.Domain.Menus { ///

造轮子之自定义授权策略

前面我们已经弄好了用户角色这块内容,接下来就是我们的授权策略。在asp.net core中提供了自定义的授权策略方案,我们可以按照需求自定义我们的权限过滤。这里我的想法是,不需要在每个Controller或者Action打上AuthorizeAttribute,自动根据ControllerName和

造轮子之消息实时推送

前面我们的EventBus已经弄好了,那么接下来通过EventBus来实现我们的消息推送就是自然而然的事情了。说到消息推送,很多人肯定会想到Websocket,既然我们使用Asp.net core,那么SignalR肯定是我们的首选。接下来就用SignalR来实现我们的消息实时推送。 Notific

造轮子之EventBus

前面基础管理的功能基本开发完了,接下来我们来优化一下开发功能,来添加EventBus功能。EventBus也是我们使用场景非常广的东西。这里我会实现一个本地的EventBus以及分布式的EventBus。分别使用MediatR和Cap来实现。 现在简单介绍一下这两者:MediatR是一个轻量级的中介

造轮子之多语言管理

多语言也是我们经常能用到的东西,asp.net core中默认支持了多语言,可以使用.resx资源文件来管理多语言配置。但是在修改资源文件后,我们的应用服务无法及时更新,属实麻烦一些。我们可以通过扩展IStringLocalizer,实现我们想要的多语言配置方式,比如Json配置,PO 文件配置,E

造轮子之asp.net core identity

在前面我们完成了应用最基础的功能支持以及数据库配置,接下来就是我们的用户角色登录等功能了,在asp.net core中原生Identity可以让我们快速完成这个功能的开发,在.NET8中,asp.net core identity支持了WebApi的注册登录。这让我们在WebApi中可以更爽快的使用

造轮子之ORM集成

Dotnet的ORM千千万,还是喜欢用EF CORE 前面一些基础完成的差不多了,接下来可以集成数据库了,官方出品的ORM还是比较香。所以接下来就是来集成EF CORE。 安装包 首先我们需要安装一下EF CORE的NUGET包,有如下几个: Microsoft.EntityFrameworkCor

动手造轮子自己实现人工智能神经网络(ANN),解决鸢尾花分类问题Golang1.18实现

人工智能神经网络( Artificial Neural Network,又称为ANN)是一种由人工神经元组成的网络结构,神经网络结构是所有机器学习的基本结构,换句话说,无论是深度学习还是强化学习都是基于神经网络结构进行构建。关于人工神经元,请参见:人工智能机器学习底层原理剖析,人造神经元,您一定能看

前端文件上传的几种交互造轮子

前端文件上传本来是一个常规交互操作,没什么特殊性可言,但是最近在做文件上传,需要实现截图粘贴上传,去找了下有没有什么好用的组件,网上提供的方法有,但是没找完整的组件来支持cv上传,经过了解发现可以用剪贴板功能让自己的cv实现文件上传,于是自己就整合了目前几种文件上传的交互方式,码了一个支持cv的vue3文件上传组件(造个轮子)。