当前位置:网站首页>ABP vNext microservice architecture detailed tutorial - distributed permission framework (Part 2)

ABP vNext microservice architecture detailed tutorial - distributed permission framework (Part 2)

2022-07-05 03:39:00 Dotnet cross platform

3

Common components

a8e6692318bf0c372a0bde144630b4b7.gif

Add public class library Demo.Permissions, edit Demo.Permissions.csproj file , take  <Project Sdk="Microsoft.NET.Sdk">  Change it to :

<Project Sdk="Microsoft.NET.Sdk.Web">

e3baff2e08eb272aa23ad73454429f8b.gif

by Demo.Permissions Project add Nuget quote Volo.Abp.Core and Microsoft.AspNetCore.Http, And Application Demo.Identity.HttpApi.Client project .

stay Demo.Permissions Add permission relation enumeration PermissionRelation as follows :

namespace Demo.Permissions;


/// <summary>
///  Permission relation enumeration 
/// </summary>
public enum PermissionRelation
{
    /// <summary>
    ///  Need to meet at the same time 
    /// </summary>
    And,
    /// <summary>
    ///  Only need to meet any one 
    /// </summary>
    Or,
        
}

1708e826748c2b46107c2ac8c47aa36c.gif

stay Demo.Permissions Add CusPermissionAttribute characteristic , Used to mark the permissions required by the interface , as follows :

namespace Demo.Permissions;


/// <summary>
///  Custom permission features 
/// </summary>
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class CusPermissionAttribute : Attribute
{
    /// <summary>
    ///  Authority code 
    /// </summary>
    public string[] PermissionCode { get; }


    /// <summary>
    ///  The relationship between permissions 
    /// </summary>
    public PermissionRelation Relation { get; } = PermissionRelation.And;
        


    /// <summary>
    ///  Constructors 
    /// </summary>
    /// <param name="relation"> Permissions on the relationship between </param>
    /// <param name="permissionCodes"> Authority code </param>
    public CusPermissionAttribute(PermissionRelation relation,params string[] permissionCodes)
    {
        Relation = relation;
        PermissionCode = permissionCodes;
    }
        
    /// <summary>
    ///  Constructors 
    /// </summary>
    /// <param name="permissionCodes"> Authority code </param>
    public CusPermissionAttribute(params string[] permissionCodes)
    {
        PermissionCode = permissionCodes;
    }
}

6a618f39fb570cc17cdc33e45d7f95db.png

One feature can declare multiple permission codes ,Relation Represents the relationship between all permission codes in this feature , If And, The user needs to have all the permission codes declared by this feature to pass the verification , if Or, It means that the user can pass the authentication as long as he has any one or more permissions declared in this feature .

df13976fe3d9d8fecb518c6f240ee5d4.png

An interface can declare multiple features , Between features is And Relationship .

69053d92705db1b81dad12a004f7c20d.gif

stay Demo.Permissions Add permission verification Middleware in CusPermissionMiddleware as follows :

using Demo.Identity.Permissions;
using Microsoft.AspNetCore.Http.Features;
using Volo.Abp.Users;


namespace Demo.Permissions;


/// <summary>
///  Custom permission middleware 
/// </summary>
public class CusPermissionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ICurrentUser _currentUser;
    private readonly ISysPermissionAppService _service;


    public CusPermissionMiddleware(RequestDelegate next, ICurrentUser currentUser, ISysPermissionAppService service)
    {
        _next = next;
        _currentUser = currentUser;
        _service = service;
    }


    public async Task InvokeAsync(HttpContext context)
    {
        var attributes = 
            context.GetEndpoint()?.Metadata.GetOrderedMetadata<CusPermissionAttribute>();
        
        // If it doesn't exist CusPermissionAttribute Feature, the interface does not need permission verification , Just skip 
        if (attributes==null||attributes.Count==0)
        {
            await _next(context);
            return;
        }
        // If permission authentication is required, it must be a logged in user , Otherwise return to 401
        if (_currentUser.Id == null)
        {
            context.Response.StatusCode = 401;
            return;
        }
        // Get user rights 
        var userPermisions = (await _service.GetUserPermissionCode((Guid) _currentUser.Id)).ToHashSet();
        // Comparison authority   If there is no permission, return 403
        foreach (var cusPermissionAttribute in attributes)
        {
            var flag = cusPermissionAttribute.Relation == PermissionRelation.And
                ? cusPermissionAttribute.PermissionCode.All(code => userPermisions.Contains(code))
                : cusPermissionAttribute.PermissionCode.Any(code => userPermisions.Contains(code));
            if (!flag)
            {
                context.Response.StatusCode = 403;
                return;
            }
        }


        await _next(context);
    }
}

When the interface is called , The middleware will obtain the permission characteristics declared by the interface , And call the identity management service interface to obtain the permission code held by the current user , Verify in order of characteristics .

19db298d13af57d0e866a5fa40307821.gif

stay Demo.Permissions Add PermissionRegistor class , It is used to read all permission codes declared in the code when the aggregation service is started , And register with the identity management service . The code is as follows :

using System.ComponentModel;
using Demo.Identity.Permissions.Dto;


namespace Demo.Permissions;


/// <summary>
///  Permission registration 
/// </summary>
public static class PermissionRegistor
{


    /// <summary>
    ///  Get the permission set in the specified type 
    /// </summary>
    /// <param name="serviceName"> The service name </param>
    /// <typeparam name="T"> type </typeparam>
    /// <returns></returns>
    internal static List<SysPermissionDto> GetPermissions<T>(string serviceName)
    {
        List<SysPermissionDto> result = new List<SysPermissionDto>();
        Type type = typeof(T);
        var fields = type.GetFields().Where(x=>x.IsPublic&&x.IsStatic);
        foreach (var field in fields)
        {
            string code = field.GetValue(null).ToString();
            string name = "";
            object[] objs = field.GetCustomAttributes(typeof(DescriptionAttribute), false);  // Get description properties 
            if (objs != null && objs.Length > 0)
            {
                DescriptionAttribute descriptionAttribute = (DescriptionAttribute) objs[0];
                name = descriptionAttribute.Description;
            }


            string parentCode = null;
            if (code.Contains("."))
            {
                parentCode = code.Substring(0, code.LastIndexOf('.'));
            }


            result.Add(new SysPermissionDto()
            {
                Name = name,
                Code = code,
                ParentCode = parentCode,
                ServiceName = serviceName,
            });
        }


        return result;
    }
}

f97ff336acafd80428e75315fc696739.gif

stay Demo.Permissions Add CusPermissionExtensions class , Provide IApplicationBuilder Extension method of , Used to register middleware and register permissions , The code is as follows :

using Demo.Identity.Permissions;


namespace Demo.Permissions;


public static class CusPermissionExtensions
{
    /// <summary>
    ///  Register custom permissions 
    /// </summary>
    public static void UseCusPermissions<T>(this IApplicationBuilder app, string serviceName)
    {
        app.RegistPermissions<T>(serviceName);
        app.UseMiddleware<CusPermissionMiddleware>();
    }


    /// <summary>
    ///  Registration rights 
    /// </summary>
    /// <param name="app"></param>
    /// <param name="serviceName"> The service name </param>
    /// <typeparam name="T"></typeparam>
    private static async Task RegistPermissions<T>(this IApplicationBuilder app, string serviceName)
    {
        var service = app.ApplicationServices.GetService<ISysPermissionAppService>();
        var permissions = PermissionRegistor.GetPermissions<T>(serviceName);
        await service.RegistPermission(serviceName, permissions);
    }
}

5f96a4268212e54dd1a8c52f49f10d73.gif

stay Demo.Permissions Add DemoPermissionsModule The categories are as follows :

using Demo.Identity;
using Volo.Abp.Modularity;


namespace Demo.Permissions;


[DependsOn(typeof(IdentityHttpApiClientModule))]
public class DemoPermissionsModule:AbpModule
{


}

4

Aggregation service layer

fdad055f055aeeabc06389d7d7a93a3e.gif

In the aggregation service layer , We can use the just created Demo.Permissions Class library , Take the mall service as an example .

bd59a07c6a9bab985d25ba7f8a4c30c3.gif

stay Demo.Store.Application Add to the project Demo.Permissions Project references for , And for DemoStoreApplicationModule Class adds the following features :

[DependsOn(typeof(DemoPermissionsModule))]

c482083c2d068b7446b8cf6218babb73.gif

stay Demo.Store.Application Added in the project PermissionLab Class is used to declare all permissions used in this service , The code is as follows

using System.ComponentModel;


namespace Demo.Store.Application;


/// <summary>
///  Permission list 
/// </summary>
public class PermissionLab
{
    [Description(" Order ")]
    public const string ORDER = "Order";


    [Description(" Create order ")]
    public const string ORDER_CREATE = $"{ORDER}.Create";


    [Description(" Query order ")]
    public const string ORDER_SELECT = $"{ORDER}.Select";


    // Add other permissions  ……
}

Here we use constants to define permissions , The value of the constant is the permission code , Constant names use Description Characteristic marks .

aa0ca02f5df2b7fce94e9d52afe9baf0.gif

stay Demo.Store.HttpApi.Host Project profile appsettings.json Medium RemoteServices Add the following address of identity management service in :

"Default": {
    "BaseUrl": "http://localhost:5000/"
},

7030cbb0262a1ad46ac0b24c415a1bca.gif

stay Demo.Store.HttpApi.Host project DemoStoreHttpApiHostModule class OnApplicationInitialization Method  app.UseRouting(); , Add the following after it :

app.UseCusPermissions<PermissionLab>("Store");

6556bdce2d12c4536a1dce92fca60425.gif

In this way, we can aggregate the service layer ApplicationService Add CusPermission Used to declare the permissions required by the interface , for example :

/// <summary>
///  Query the order list in pages 
/// </summary>
/// <param name="input"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
[CusPermission(PermissionLab.ORDER_SELECT)]
public async Task<PagedResultDto<StoreOrderDto>> GetListAsync(PagedAndSortedResultRequestDto input)
{
  var ret = await _orderAppService.GetListAsync(input);
  return new PagedResultDto<StoreOrderDto>
  {
    TotalCount = ret.TotalCount,
    Items = ObjectMapper.Map<IReadOnlyList<OrderDto>, List<StoreOrderDto>>(ret.Items)
  };
}

5

Additional explanation

25a119d0e8e9f2c8dae1127725af7af1.png

After completing the above steps , We can aggregate the service layer Admin The project encapsulates and exposes the role permission related interface in the identity management service to the client call , The registration permission interface is only used to register permissions for the aggregation service layer , It is not recommended to expose to clients .

6c4a10f751b6253ade9a9ba94ced9a29.png

Here I simply use the verification of the permission code itself , There is no correlation verification of parent-child relationship , In the actual project , It can be modified or extended as needed .

aae4a32f2c6b79983d160eec13e79a5b.png

end

705dc802d107d1fa8d46fba94aa688cb.png

f62dd36aff00aefb7a9314524f51145b.png

ed9d266f3ad71f7e9e430ff917b9393c.png

More exciting

Follow me to get

72143ccc505e58c206bbe6565fed3f81.png

原网站

版权声明
本文为[Dotnet cross platform]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202140728263926.html