上文已經完成了自定義授權策略,那麼接下來就得完善我們的許可權管理了。不然沒有資料,如何鑑權~
表設計
建立我們的表實體類:
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
歡迎進群催更。