上文已经完成了自定义授权策略,那么接下来就得完善我们的权限管理了。不然没有数据,如何鉴权~
表设计
创建我们的表实体类:
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注释的内容。
到这我们就完成了权限管理的API。
轮子仓库地址https://github.com/Wheel-Framework/Wheel
欢迎进群催更。
