1.asp.net core入门到造轮子-目录2.asp.net core之Startup3.asp.net core之依赖注入4.asp.net core之中间件5.asp.net core之Host6.asp.net core之Kestrel7.asp.net core之配置8.asp.net core之Options9.asp.net core之日志10.asp.net core之路由11.asp.net core之异常处理12.asp.net core之HttpClient13.asp.net core之实时应用14.asp.net core之EfCore15.造轮子之自动依赖注入16.造轮子之日志17.造轮子之统一业务异常处理18.造轮子之统一请求响应格式19.造轮子之缓存20.造轮子之ORM集成21.造轮子之asp.net core identity22.造轮子之自定义授权策略
23.造轮子之权限管理
24.造轮子之多语言管理25.造轮子之角色管理26.造轮子之用户管理27.造轮子之菜单管理28.造轮子之属性注入配合懒加载构建服务抽象基类29.造轮子之EventBus30.造轮子之消息实时推送31.造轮子之种子数据32.造轮子之集成GraphQL33.造轮子之设置管理34.造轮子之文件管理上文已经完成了自定义授权策略,那么接下来就得完善我们的权限管理了。不然没有数据,如何鉴权~
表设计#
创建我们的表实体类:
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注释的内容。
到这我们就完成了权限管理的API。