ABP vNext微服务架构详细教程——分布式权限框架(下)
3
公共組件
添加公共類(lèi)庫(kù)Demo.Permissions,編輯Demo.Permissions.csproj文件,將?<Project Sdk="Microsoft.NET.Sdk">?改為:
<Project Sdk="Microsoft.NET.Sdk.Web">為Demo.Permissions項(xiàng)目添加Nuget引用Volo.Abp.Core和Microsoft.AspNetCore.Http,并應(yīng)用Demo.Identity.HttpApi.Client項(xiàng)目。
在Demo.Permissions中添加權(quán)限關(guān)系枚舉PermissionRelation如下:
namespace Demo.Permissions;/// <summary> /// 權(quán)限關(guān)系枚舉 /// </summary> public enum PermissionRelation {/// <summary>/// 需要同時(shí)滿足/// </summary>And,/// <summary>/// 只需要滿足任意一項(xiàng)/// </summary>Or,}在Demo.Permissions中添加CusPermissionAttribute特性,用于標(biāo)記接口所需要的權(quán)限,如下:
namespace Demo.Permissions;/// <summary> /// 自定義權(quán)限特性 /// </summary> [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] public class CusPermissionAttribute : Attribute {/// <summary>/// 權(quán)限編碼/// </summary>public string[] PermissionCode { get; }/// <summary>/// 權(quán)限之間的關(guān)系/// </summary>public PermissionRelation Relation { get; } = PermissionRelation.And;/// <summary>/// 構(gòu)造函數(shù)/// </summary>/// <param name="relation">權(quán)限關(guān)系</param>/// <param name="permissionCodes">權(quán)限編碼</param>public CusPermissionAttribute(PermissionRelation relation,params string[] permissionCodes){Relation = relation;PermissionCode = permissionCodes;}/// <summary>/// 構(gòu)造函數(shù)/// </summary>/// <param name="permissionCodes">權(quán)限編碼</param>public CusPermissionAttribute(params string[] permissionCodes){PermissionCode = permissionCodes;} }其中一個(gè)特性可以聲明多個(gè)權(quán)限碼,Relation表示該特性中所有權(quán)限碼間的關(guān)系,如果為And,需要用戶具有該特性聲明的所有權(quán)限碼才可通過(guò)驗(yàn)證,若為Or,則表示用戶只要具有任意一個(gè)或多個(gè)該特性中聲明的權(quán)限就可通過(guò)驗(yàn)證。
一個(gè)接口可以聲明多個(gè)特性,特性與特性之間是And關(guān)系。
在Demo.Permissions中添加權(quán)限驗(yàn)證中間件CusPermissionMiddleware如下:
using Demo.Identity.Permissions; using Microsoft.AspNetCore.Http.Features; using Volo.Abp.Users;namespace Demo.Permissions;/// <summary> /// 自定義權(quán)限中間件 /// </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>();//如果不存在CusPermissionAttribute特性則該接口不需要權(quán)限驗(yàn)證,直接跳過(guò)if (attributes==null||attributes.Count==0){await _next(context);return;}//如果需要權(quán)限驗(yàn)證則必須是已登錄用戶,否則返回401if (_currentUser.Id == null){context.Response.StatusCode = 401;return;}//獲取用戶權(quán)限var userPermisions = (await _service.GetUserPermissionCode((Guid) _currentUser.Id)).ToHashSet();//比對(duì)權(quán)限 如果無(wú)權(quán)限則返回403foreach (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);} }在接口調(diào)用時(shí),該中間件會(huì)獲取接口所聲明的權(quán)限特性,并調(diào)用身份管理服務(wù)接口獲取當(dāng)前用戶所持有的權(quán)限碼,按特性順序依次驗(yàn)證。
在Demo.Permissions中添加PermissionRegistor類(lèi),用于在聚合服務(wù)啟動(dòng)時(shí)讀取代碼中聲明的所有權(quán)限碼,并注冊(cè)到身份管理服務(wù)。代碼如下:
using System.ComponentModel; using Demo.Identity.Permissions.Dto;namespace Demo.Permissions;/// <summary> /// 權(quán)限注冊(cè) /// </summary> public static class PermissionRegistor {/// <summary>/// 在指定類(lèi)型中獲取權(quán)限集合/// </summary>/// <param name="serviceName">服務(wù)名稱(chēng)</param>/// <typeparam name="T">類(lèi)型</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); //獲取描述屬性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;} }在Demo.Permissions中添加CusPermissionExtensions類(lèi),提供IApplicationBuilder的擴(kuò)展方法,用于注冊(cè)中間件和注冊(cè)權(quán)限,代碼如下:
using Demo.Identity.Permissions;namespace Demo.Permissions;public static class CusPermissionExtensions {/// <summary>/// 注冊(cè)自定義權(quán)限/// </summary>public static void UseCusPermissions<T>(this IApplicationBuilder app, string serviceName){app.RegistPermissions<T>(serviceName);app.UseMiddleware<CusPermissionMiddleware>();}/// <summary>/// 注冊(cè)權(quán)限/// </summary>/// <param name="app"></param>/// <param name="serviceName">服務(wù)名稱(chēng)</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);} }在Demo.Permissions中添加DemoPermissionsModule類(lèi)如下:
using Demo.Identity; using Volo.Abp.Modularity;namespace Demo.Permissions;[DependsOn(typeof(IdentityHttpApiClientModule))] public class DemoPermissionsModule:AbpModule {}4
聚合服務(wù)層
在聚合服務(wù)層,我們就可以使用剛才創(chuàng)建的Demo.Permissions類(lèi)庫(kù),這里以商城服務(wù)為例。
在Demo.Store.Application項(xiàng)目中添加Demo.Permissions的項(xiàng)目引用,并為DemoStoreApplicationModule類(lèi)添加以下特性:
[DependsOn(typeof(DemoPermissionsModule))]在Demo.Store.Application項(xiàng)目中添加在PermissionLab類(lèi)用于聲明該服務(wù)中用到的所有權(quán)限,代碼如下
using System.ComponentModel;namespace Demo.Store.Application;/// <summary> /// 權(quán)限列表 /// </summary> public class PermissionLab {[Description("訂單")]public const string ORDER = "Order";[Description("創(chuàng)建訂單")]public const string ORDER_CREATE = $"{ORDER}.Create";[Description("查詢(xún)訂單")]public const string ORDER_SELECT = $"{ORDER}.Select";//添加其他權(quán)限 …… }這里使用常量定義權(quán)限,其中常量的值為權(quán)限碼,常量名稱(chēng)使用Description特性標(biāo)記。
在Demo.Store.HttpApi.Host項(xiàng)目配置文件appsettings.json中的RemoteServices中添加身份管理服務(wù)地址如下:
"Default": {"BaseUrl": "http://localhost:5000/" },在Demo.Store.HttpApi.Host項(xiàng)目DemoStoreHttpApiHostModule類(lèi)OnApplicationInitialization方法中找到?app.UseRouting();?,在其后面添加如下內(nèi)容:
app.UseCusPermissions<PermissionLab>("Store");這樣我們就可以在聚合服務(wù)層ApplicationService的方法上添加CusPermission用于聲明接口所需要的權(quán)限,例如:
/// <summary> /// 分頁(yè)查詢(xún)訂單列表 /// </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
補(bǔ)充說(shuō)明
完成以上步驟后,我們可以在聚合服務(wù)層Admin項(xiàng)目中將身份管理服務(wù)中角色權(quán)限相關(guān)接口封裝并暴露給客戶端調(diào)用,其中注冊(cè)權(quán)限接口僅為聚合服務(wù)層注冊(cè)權(quán)限使用,不建議暴露給客戶端。
這里我只簡(jiǎn)單使用了對(duì)權(quán)限碼自身的校驗(yàn),并未做父子關(guān)系的關(guān)聯(lián)校驗(yàn),在實(shí)際項(xiàng)目中,可以依據(jù)需要進(jìn)行修改或擴(kuò)展。
end
更多精彩
關(guān)注我獲得
總結(jié)
以上是生活随笔為你收集整理的ABP vNext微服务架构详细教程——分布式权限框架(下)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: C# 使用Awaiter
- 下一篇: Elasticsearch数据库