当前位置:网站首页>ABP vNext microservice architecture detailed tutorial - distributed permission framework (Part 1)
ABP vNext microservice architecture detailed tutorial - distributed permission framework (Part 1)
2022-07-05 03:39:00 【Dotnet cross platform】
1
brief introduction
ABP vNext The framework itself provides a set of permission framework , Its functions are very rich , For details, please refer to the official documents :https://docs.abp.io/en/abp/latest/Authorization
But when we use it, we will find , For normal monomer application ,ABP vNext There is no problem with the permission system provided by the framework , But under the microservice Architecture , This permission system is not very friendly .
I hope my permission system can meet the following requirements :
Each aggregation service holds an independent set of permissions
Each aggregation service can be declared independently 、 Use its interface to access the required permissions .
Provide a unified interface to manage 、 Store all service permissions and authorize roles .
Each interface can flexibly combine and use one or more permission codes .
Use the permission framework as simple as possible , Reduce the amount of extra coding .
stay ABP vNext Based on the framework , Rewrite a set of distributed permission framework , The general rules are as follows :
Use ABP vNext Users provided in the framework 、 The role model does not change , Instead, redefine the permission model , Redefine the entities of permissions and related service interfaces .
In identity management services , Realize unified management of permissions 、 Role authorization and authority authentication .
Define the permission information it has in the aggregation service 、 Permission relationship and declare the permissions required by each interface through characteristics .
When the aggregation service starts , Automatically register its permission information to the identity management service .
When the client accesses the aggregated service layer service, verify whether the current user has the interface permission in the aggregated service layer middleware , The authentication process needs to call the corresponding interface of the identity management service .
The specific implementation of permission system is shown below .
2
Identity authentication service
In the previous article, we have built the basic framework of identity authentication service , Here we add code directly on this basis .
stay Demo.Identity.Domain Add to the project Permissions Folder , And add Entities A folder . Add entity classes under this folder SysPermission and RolePermissions as follows :
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Domain.Entities;
namespace Demo.Identity.Permissions.Entities;
/// <summary>
/// Permission entity class
/// </summary>
public class SysPermission : Entity<Guid>
{
/// <summary>
/// The service name
/// </summary>
[MaxLength(64)]
public string ServiceName { get; set; }
/// <summary>
/// Authority code
/// </summary>
[MaxLength(128)]
public string Code { get; set; }
/// <summary>
/// Permission to name
/// </summary>
[MaxLength(64)]
public string Name { get; set; }
/// <summary>
/// Superior authority ID
/// </summary>
[MaxLength(128)]
public string ParentCode { get; set; }
/// <summary>
/// Judge whether the two permissions are the same
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public override bool Equals(object? obj)
{
return obj is SysPermission permission
&& permission.ServiceName == ServiceName
&& permission.Name == Name
&& permission.Code == Code
&& permission.ParentCode == ParentCode;
}
/// <summary>
/// Set up ID Value
/// </summary>
/// <param name="id"></param>
public void SetId(Guid id)
{
Id = id;
}
}
using System;
using Volo.Abp.Domain.Entities;
namespace Demo.Identity.Permissions.Entities;
/// <summary>
/// Role permission correspondence
/// </summary>
public class RolePermissions : Entity<Guid>
{
/// <summary>
/// Role number
/// </summary>
public Guid RoleId { get; set; }
/// <summary>
/// Authority number
/// </summary>
public Guid PermissionId { get; set; }
}
take Demo.Identity.Application.Contracts The project has Permissions Delete all classes in the folder , And add subfolders Dto. Add... Under this folder SysPermissionDto、PermissionTreeDto、SetRolePermissionsDto
The categories are as follows :
using System;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.Application.Dtos;
namespace Demo.Identity.Permissions.Dto;
/// <summary>
/// jurisdiction DTO
/// </summary>
public class SysPermissionDto:EntityDto<Guid>
{
/// <summary>
/// The service name
/// </summary>
[MaxLength(64)]
public string ServiceName { get; set; }
/// <summary>
/// Authority code
/// </summary>
[MaxLength(128)]
public string Code { get; set; }
/// <summary>
/// Permission to name
/// </summary>
[MaxLength(64)]
public string Name { get; set; }
/// <summary>
/// Superior authority ID
/// </summary>
[MaxLength(128)]
public string ParentCode { get; set; }
}
using System;
using System.Collections.Generic;
using Volo.Abp.Application.Dtos;
namespace Demo.Identity.Permissions.Dto;
/// <summary>
/// Permission tree DTO
/// </summary>
public class PermissionTreeDto : EntityDto<Guid>
{
/// <summary>
/// The service name
/// </summary>
public string ServiceName { get; set; }
/// <summary>
/// Authority code
/// </summary>
public string Code { get; set; }
/// <summary>
/// Permission to name
/// </summary>
public string Name { get; set; }
/// <summary>
/// Superior authority ID
/// </summary>
public string ParentCode { get; set; }
/// <summary>
/// Sub permission
/// </summary>
public List<PermissionTreeDto> Children { get; set; }
}
using System;
using System.Collections.Generic;
namespace Demo.Identity.Permissions.Dto;
/// <summary>
/// Set role permissions DTO
/// </summary>
public class SetRolePermissionsDto
{
/// <summary>
/// Role number
/// </summary>
public Guid RoleId { get; set; }
/// <summary>
/// jurisdiction ID list
/// </summary>
public List<Guid> Permissions { get; set; }
}
take Demo.Identity.Application.Contracts In the project Permissions Add an interface under the folder IRolePermissionsAppService as follows :
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Demo.Identity.Permissions.Dto;
using Volo.Abp.Application.Services;
namespace Demo.Identity.Permissions;
/// <summary>
/// Role management application service interface
/// </summary>
public interface IRolePermissionsAppService
: IApplicationService
{
/// <summary>
/// Get all permissions of the role
/// </summary>
/// <param name="roleId"> role ID</param>
/// <returns></returns>
Task<List<PermissionTreeDto>> GetPermission(Guid roleId);
/// <summary>
/// Set role permissions
/// </summary>
/// <param name="dto"> Role permission information </param>
/// <returns></returns>
Task SetPermission(SetRolePermissionsDto dto);
}
take Demo.Identity.Application.Contracts In the project Permissions Add an interface under the folder ISysPermissionAppService as follows :
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Demo.Identity.Permissions.Dto;
using Volo.Abp.Application.Services;
namespace Demo.Identity.Permissions;
/// <summary>
/// Permission management application service interface
/// </summary>
public interface ISysPermissionAppService:IApplicationService
{
/// <summary>
/// Register permissions by service
/// </summary>
/// <param name="serviceName"> The service name </param>
/// <param name="permissions"> Permission list </param>
/// <returns></returns>
Task<bool> RegistPermission(string serviceName, List<SysPermissionDto> permissions);
/// <summary>
/// Get permissions by service
/// </summary>
/// <param name="serviceName"> The service name </param>
/// <returns> Query results </returns>
Task<List<SysPermissionDto>> GetPermissions(string serviceName);
/// <summary>
/// Get the full permission tree
/// </summary>
/// <param name="Permission"></param>
/// <returns> Query results </returns>
Task<List<PermissionTreeDto>> GetPermissionTree();
/// <summary>
/// Get user permission code
/// </summary>
/// <param name="userId"> The user id </param>
/// <returns> Query results </returns>
Task<List<string>> GetUserPermissionCode(Guid userId);
}
In the public class library folder common Created in .Net6 Class library project Demo.Core, Used to store general classes .
Here we are Demo.Core Add folder in CommonExtension Used to store general extensions , add to EnumExtensions and ListExtensions The categories are as follows :
namespace Demo.Core.CommonExtension;
/// <summary>
/// Enumerating extension classes
/// </summary>
public static class EnumExtensions
{
/// <summary>
/// Get description properties
/// </summary>
/// <param name="enumValue"> Enumerated values </param>
/// <returns></returns>
public static string GetDescription(this Enum enumValue)
{
string value = enumValue.ToString();
FieldInfo field = enumValue.GetType().GetField(value);
object[] objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false); // Get description properties
if (objs == null || objs.Length == 0) // When there is no description attribute , Directly return the name
return value;
DescriptionAttribute descriptionAttribute = (DescriptionAttribute)objs[0];
return descriptionAttribute.Description;
}
}
namespace Demo.Core.CommonExtension;
public static class ListExtensions
{
/// <summary>
/// Gather to get rid of the heavy
/// </summary>
/// <param name="lst"> Target set </param>
/// <param name="keySelector"> Remove keywords </param>
/// <typeparam name="T"> Collection element type </typeparam>
/// <typeparam name="TKey"> De duplication keyword data type </typeparam>
/// <returns> De duplication results </returns>
public static List<T> Distinct<T,TKey>(this List<T> lst,Func<T, TKey> keySelector)
{
List<T> result = new List<T>();
HashSet<TKey> set = new HashSet<TKey>();
foreach (var item in lst)
{
var key = keySelector(item);
if (!set.Contains(key))
{
set.Add(key);
result.Add(item);
}
}
return result;
}
}
stay Demo.Core Add a folder to the project CommonFunction For storing general methods , Here we add ListCompare The categories are as follows :
using VI.Core.CommonExtension;
namespace VI.Core.CommonFunction;
/// <summary>
/// Set comparison
/// </summary>
public class ListCompare
{
/*
* Call the instance :
* MutiCompare<Permission, string>(lst1, lst2, x => x.Code, (obj, isnew) =>
* {
* if (isnew)
* {
* Console.WriteLine($" What's new {obj.Id}");
* }
* else
* {
* Console.WriteLine($" Already exists {obj.Id}");
* }
* }, out var lstNeedRemove);
*/
/// <summary>
/// Compare the source set with the target set , Deal with existing items and new items , And find the items that need to be deleted
/// </summary>
/// <param name="lstSource"> Source set </param>
/// <param name="lstDestination"> Target set </param>
/// <param name="keySelector"> Set comparison keywords </param>
/// <param name="action"> New or existing item processing methods , Parameters :( Data item , Whether to add )</param>
/// <param name="needRemove"> Data set to be deleted </param>
/// <typeparam name="TObject"> Collection object data type </typeparam>
/// <typeparam name="TKey"> Compare keyword data types </typeparam>
public static void MutiCompare<TObject,TKey>(List<TObject> lstDestination,List<TObject> lstSource,
Func<TObject, TKey> keySelector,
Action<TObject, bool> action,
out Dictionary<TKey, TObject> needRemove)
{
// Target set de duplication
lstDestination.Distinct(keySelector);
// Store the source set in the dictionary , Improve query efficiency
needRemove = new Dictionary<TKey, TObject>();
foreach (var item in lstSource)
{
needRemove.Add(keySelector(item),item);
}
// Traverse the target set , Distinguish between new items and existing items
// Exclude items in the target set from the dictionary , The remaining items are the items to be deleted in the source collection
foreach (var item in lstDestination)
{
if (needRemove.ContainsKey(keySelector(item)))
{
action(item, false);
needRemove.Remove(keySelector(item));
}
else
{
action(item, true);
}
}
}
}
stay Demo.Identity.Application Add to the project Permissions Folder .
stay Demo.Identity.Application project Permissions Add in folder PermissionProfileExtensions Class is used to define object mapping relationships as follows :
using Demo.Identity.Permissions.Dto;
using Demo.Identity.Permissions.Entities;
namespace Demo.Identity.Permissions;
public static class PermissionProfileExtensions
{
/// <summary>
/// Create entity mapping relationships related to permission fields
/// </summary>
/// <param name="profile"></param>
public static void CreatePermissionsMap(this IdentityApplicationAutoMapperProfile profile)
{
profile.CreateMap<SysPermission, PermissionTreeDto>();
profile.CreateMap<SysPermission,SysPermissionDto>();
profile.CreateMap<SysPermissionDto,SysPermission>();
}
}
stay Demo.Identity.Application project IdentityApplicationAutoMapperProfile Class IdentityApplicationAutoMapperProfile Add the following code to the method :
this.CreatePermissionsMap();
stay Demo.Identity.Application project Permissions Add in folder PermissionTreeBuilder class , The general method of defining and constructing permission tree structure is as follows :
using System.Collections.Generic;
using System.Linq;
using Demo.Identity.Permissions.Dto;
namespace Demo.Identity.Permissions;
/// <summary>
/// Permission tree help class
/// </summary>
public static class PermissionTreeBuilder
{
/// <summary>
/// Build a tree structure
/// </summary>
/// <param name="lst"></param>
/// <returns></returns>
public static List<PermissionTreeDto> Build(List<PermissionTreeDto> lst)
{
var result = lst.ToList();
for (var i = 0; i < result.Count; i++)
{
if (result[i].ParentCode == null)
{
continue;
}
foreach (var item in lst)
{
item.Children ??= new List<PermissionTreeDto>();
if (item.Code != result[i].ParentCode)
{
continue;
}
item.Children.Add(result[i]);
result.RemoveAt(i);
i--;
break;
}
}
return result;
}
}
Then we were in Demo.Identity.Application project Permissions Add the permission management implementation class in the folder SysPermissionAppService as follows :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Demo.Core.CommonFunction;
using Demo.Identity.Permissions.Dto;
using Demo.Identity.Permissions.Entities;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Demo.Core.CommonExtension;
namespace Demo.Identity.Permissions
{
/// <summary>
/// Rights management application service
/// </summary>
public class SysPermissionAppService : IdentityAppService, ISysPermissionAppService
{
#region initialization
private readonly IRepository<RolePermissions> _rolePermissionsRepository;
private readonly IRepository<SysPermission> _sysPermissionsRepository;
private readonly IRepository<IdentityUserRole> _userRolesRepository;
public SysPermissionAppService(
IRepository<RolePermissions> rolePermissionsRepository,
IRepository<SysPermission> sysPermissionsRepository,
IRepository<IdentityUserRole> userRolesRepository
)
{
_rolePermissionsRepository = rolePermissionsRepository;
_sysPermissionsRepository = sysPermissionsRepository;
_userRolesRepository = userRolesRepository;
}
#endregion
#region Register permissions by service
/// <summary>
/// Register permissions by service
/// </summary>
/// <param name="serviceName"> The service name </param>
/// <param name="permissions"> Permission list </param>
/// <returns></returns>
public async Task<bool> RegistPermission(string serviceName, List<SysPermissionDto> permissions)
{
// Query existing permissions by service name
var entities = await AsyncExecuter.ToListAsync(
(await _sysPermissionsRepository.GetQueryableAsync()).Where(c => c.ServiceName == serviceName)
);
var lst = ObjectMapper.Map<List<SysPermissionDto>, List<SysPermission>>(permissions);
ListCompare.MutiCompare(lst, entities, x => x.Code, async (entity, isNew) =>
{
if (isNew)
{
// newly added
await _sysPermissionsRepository.InsertAsync(entity);
}
else
{
// modify
var tmp = lst.FirstOrDefault(x => x.Code == entity.Code);
// Call the permission judgment method , If code and name If it is the same, it will not be added
if (!entity.Equals(tmp)&&tmp!=null)
{
entity.SetId(tmp.Id);
await _sysPermissionsRepository.UpdateAsync(entity);
}
}
}, out var needRemove);
foreach (var item in needRemove)
{
// Delete extra items
await _sysPermissionsRepository.DeleteAsync(item.Value);
}
return true;
}
#endregion
#region Get permissions by service
/// <summary>
/// Get permissions by service
/// </summary>
/// <param name="serviceName"> The service name </param>
/// <returns> Query results </returns>
public async Task<List<SysPermissionDto>> GetPermissions(string serviceName)
{
var query = (await _sysPermissionsRepository.GetQueryableAsync()).Where(x => x.ServiceName == serviceName);
// Use AsyncExecuter Do asynchronous queries
var lst = await AsyncExecuter.ToListAsync(query);
// Map entity classes to dto
return ObjectMapper.Map<List<SysPermission>, List<SysPermissionDto>>(lst);
}
#endregion
#region Get the full permission tree
/// <summary>
/// Get the full permission tree
/// </summary>
/// <returns> Query results </returns>
public async Task<List<PermissionTreeDto>> GetPermissionTree()
{
var per = await _sysPermissionsRepository.ToListAsync();
var lst = ObjectMapper.Map<List<SysPermission>, List<PermissionTreeDto>>(per);
return PermissionTreeBuilder.Build(lst);
}
#endregion
#region Get user permission code
/// <summary>
/// Get user permission code
/// </summary>
/// <param name="userId"> The user id </param>
/// <returns> Query results </returns>
public async Task<List<string>> GetUserPermissionCode(Guid userId)
{
var query = from user in (await _userRolesRepository.GetQueryableAsync()).Where(c => c.UserId == userId)
join rp in (await _rolePermissionsRepository.GetQueryableAsync()) on user.RoleId equals rp.RoleId
join pe in (await _sysPermissionsRepository.GetQueryableAsync()) on rp.PermissionId equals pe.Id
select pe.Code;
var permission = await AsyncExecuter.ToListAsync(query);
return permission.Distinct(x=>x);
}
#endregion
}
}
Add the role permission relationship management implementation class RolePermissionsAppService as follows :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Demo.Identity.Permissions.Dto;
using Demo.Identity.Permissions.Entities;
using Volo.Abp.Domain.Repositories;
namespace Demo.Identity.Permissions
{
/// <summary>
/// Role management application service
/// </summary>
public class RolePermissionsAppService : IdentityAppService, IRolePermissionsAppService
{
#region initialization
private readonly IRepository<RolePermissions> _rolePermissionsRepository;
private readonly IRepository<SysPermission> _sysPermissionsRepository;
public RolePermissionsAppService(
IRepository<RolePermissions> rolePermissionsRepository,
IRepository<SysPermission> sysPermissionsRepository
)
{
_rolePermissionsRepository = rolePermissionsRepository;
_sysPermissionsRepository = sysPermissionsRepository;
}
#endregion
#region Get all permissions of the role
/// <summary>
/// Get all permissions of the role
/// </summary>
/// <param name="roleId"> role ID</param>
/// <returns></returns>
public async Task<List<PermissionTreeDto>> GetPermission(Guid roleId)
{
var query = from rp in (await _rolePermissionsRepository.GetQueryableAsync())
.Where(x => x.RoleId == roleId)
join permission in (await _sysPermissionsRepository.GetQueryableAsync())
on rp.PermissionId equals permission.Id
select permission;
var permissions = await AsyncExecuter.ToListAsync(query);
var lst = ObjectMapper.Map<List<SysPermission>, List<PermissionTreeDto>>(permissions);
return PermissionTreeBuilder.Build(lst);
}
#endregion
#region Set role permissions
/// <summary>
/// Set role permissions
/// </summary>
/// <param name="roleId"> Orange number </param>
/// <param name="permissions"> Authority number </param>
/// <returns></returns>
public async Task SetPermission(SetRolePermissionsDto dto)
{
await _rolePermissionsRepository.DeleteAsync(x => x.RoleId == dto.RoleId);
foreach (var permissionId in dto.Permissions)
{
RolePermissions entity = new RolePermissions()
{
PermissionId = permissionId,
RoleId = dto.RoleId,
};
await _rolePermissionsRepository.InsertAsync(entity);
}
}
#endregion
}
}
stay Demo.Identity.EntityFrameworkCore project IdentityDbContext Add the following attributes to the class :
public DbSet<SysPermission> SysPermissions { get; set; }
public DbSet<RolePermissions> RolePermissions { get; set; }
stay Demo.Identity.EntityFrameworkCore Start the command prompt under the project directory , Execute the following commands to create and execute data migration :
dotnet-ef migrations add AddPermissions
dotnet-ef database update
stay Demo.Identity.EntityFrameworkCore project IdentityEntityFrameworkCoreModule class ConfigureServices Method options.AddDefaultRepositories(includeAllEntities: true); , Add the following code after it :
options.AddDefaultRepository<IdentityUserRole>();
Run the identity management service after completion , It can operate normally and access all interfaces , Then the basic service layer is modified . See the next article for follow-up
end
More exciting
Follow me to get
边栏推荐
- What is the most effective way to convert int to string- What is the most efficient way to convert an int to a String?
- 为什么腾讯阿里等互联网大厂诞生的好产品越来越少?
- How rem is used
- 001 chip test
- 有个疑问 flink sql cdc 的话可以设置并行度么, 并行度大于1会有顺序问题吧?
- Anti debugging (basic principles of debugger Design & NT NP and other anti debugging principles)
- 040. (2.9) relieved
- Monitoring web performance with performance
- Performance of calling delegates vs methods
- ICSI213/IECE213 Data Structures
猜你喜欢
The architect started to write a HelloWorld
Talk about the SQL server version of DTM sub transaction barrier function
Easy processing of ten-year futures and stock market data -- Application of tdengine in Tongxinyuan fund
Quick start of UI component development of phantom engine [umg/slate]
A brief introduction to the behavior tree of unity AI
Class inheritance in C #
Jd.com 2: how to prevent oversold in the deduction process of commodity inventory?
为什么腾讯阿里等互联网大厂诞生的好产品越来越少?
Why do some programmers change careers before they are 30?
【web审计-源码泄露】获取源码方法,利用工具
随机推荐
[wp][入门]刷弱类型题目
Monitoring web performance with performance
Linux Installation redis
线程基础知识
Single box check box
MySQL winter vacation self-study 2022 11 (9)
Azkaban overview
Yuancosmic ecological panorama [2022 latest]
Machine learning experiment report 1 - linear model, decision tree, neural network part
About authentication services (front and back, login, registration and exit, permission management)
ICSI213/IECE213 Data Structures
Kubernetes - identity and authority authentication
El select, El option drop-down selection box
De debugging (set the main thread as hidden debugging to destroy the debugging Channel & debugger detection)
An elegant program for Euclid‘s algorithm
Difference between MotionEvent. getRawX and MotionEvent. getX
Pat grade a 1119 pre- and post order traversals (30 points)
花了2晚,拿到了吴恩达@斯坦福大学的机器学习课程证书
【软件逆向-分析工具】反汇编和反编译工具
Redis6-01nosql database