1、前言


            但凡業(yè)務(wù)系統(tǒng),授權(quán)是繞不開的一環(huán)。見過太多只在前端做菜單及按鈕顯隱控制,但后端裸奔的,覺著前端看不到,系統(tǒng)就安全,掩耳盜鈴也好,自欺欺人也罷,這里不做評論。在.NET
          CORE中,也見過不少用操作過濾器來實現(xiàn)業(yè)務(wù)用例權(quán)限控制的,至少算是對后端做了權(quán)限控制。


            但我們知道,操作過濾器,已經(jīng)算是過濾器管道中最靠后的,基本上緊挨著我們控制器方法執(zhí)行那里了,本身,操作過濾器也不是做權(quán)限控制的地方,雖然本身它能達(dá)到權(quán)限控制效果。為什么這么說,試想下,在過濾器管道之前,還有中間件處理管道,即便是過濾器管道執(zhí)行環(huán)節(jié),操作過濾器也是最靠后的,它往前還有授權(quán)過濾器,資源過濾器等,假如我在資源過濾器中緩存了請求結(jié)果,那權(quán)限控制基本上就廢了。

            說這么多,只想表達(dá)一點(diǎn),合適的地方,合適的東西,干合適的事兒。在.NET
          CORE中,官方推薦用策略去實現(xiàn)授權(quán)。策略授權(quán),是在授權(quán)中間件環(huán)節(jié)執(zhí)行,當(dāng)然能解決上述執(zhí)行流程先后順序的問題。但如果要直接應(yīng)用于我們業(yè)務(wù)系統(tǒng)中的權(quán)限控制,恐怕遠(yuǎn)遠(yuǎn)不夠,因為你不可能為每個api用例創(chuàng)建一個角色或策略,更主要的,權(quán)限控制還要動態(tài)授予或回收的,不做擴(kuò)展直接照搬,你是很難搞的。接下來,我們就來看看,如何基于core的授權(quán)機(jī)制,去實現(xiàn)我們傳統(tǒng)的用戶,角色,權(quán)限,及權(quán)限的動態(tài)授予與回收控制。

          2、實現(xiàn)

            我們先看看,菜單表概覽:



          ?

          ?

            查詢中IsMenu代表是側(cè)邊欄菜單還是功能按鈕,這里我把按鈕級別的給篩選出來了,每個按鈕菜單都代表一個業(yè)務(wù)用例,也對應(yīng)我們一個控制器方法。
          Code是唯一的,待會兒權(quán)限控制標(biāo)識,會采用這個字段。當(dāng)然你用主鍵ID也可以,但比較難記。實際運(yùn)維中,會把這些菜單按照業(yè)務(wù)分配給指定角色,再把指定角色分配給系統(tǒng)用戶。

            接下來,定義一個Requirement,以權(quán)限碼作為主校驗對象:
          public class PermissionRequirement : IAuthorizationRequirement { public
          PermissionRequirement(params string[] codes) { this.Codes = codes; } public
          string[] Codes { get; private set; } }
            有Requirement,自然有RequirementHandler:
          1 public class PermissionHandler : AuthorizationHandler<PermissionRequirement>
          2 { 3 private readonly IMenuService _menuService; 4 5 public
          PermissionHandler(IMenuService menuService) 6 { 7 _menuService = menuService;
          8 } 9 10 protected override async Task HandleRequirementAsync( 11
          AuthorizationHandlerContext context,12 PermissionRequirement requirement) 13 {
          14 var roles = context.User.Claims.FirstOrDefault(x => x.Type ==
          ClaimTypes.Role).Value;15 if (!string.IsNullOrWhiteSpace(roles)) 16 { 17 var
          roleIds = roles.Split(',', StringSplitOptions.RemoveEmptyEntries) 18 .Select(x
          =>long.Parse(x)); 19 if (roleIds.Contains(1)) 20 { 21
          context.Succeed(requirement);22 return; 23 } 24 25 var menus = await
          _menuService.GetMenusByRoleIds(roleIds.ToArray());26 if (menus.Any()) 27 { 28
          var codes = menus.Select(x => x.Code.ToUpper()); 29 if (requirement.Codes.Any(x
          => codes.Contains(x.ToUpper()))) 30 { 31 context.Succeed(requirement); 32 }
          33 } 34 } 35 } 36 }

            邏輯比較常規(guī),根據(jù)當(dāng)前用戶角色,獲取其所有菜單權(quán)限,然后與Requirement中聲明要求的菜單權(quán)限做對比,如果含有,則放行。到這兒,大家應(yīng)該都能看懂,典型的.NET
          CORE權(quán)限控制組件。

            接下來,定義一個授權(quán)過濾器特性:
          1 public class PermissionFilter : Attribute, IAsyncAuthorizationFilter 2 {
          3 private readonly IAuthorizationService _authorizationService; 4 private
          readonly PermissionRequirement _requirement; 5 6 public
          PermissionFilter(IAuthorizationService authorizationService,
          PermissionRequirement requirement) 7 { 8 _authorizationService =
          authorizationService; 9 _requirement = requirement; 10 } 11 12 public async
          Task OnAuthorizationAsync(AuthorizationFilterContext context)13 { 14 var
          result =await _authorizationService.AuthorizeAsync(context.HttpContext.User,
          null, _requirement); 15 16 if (!result.Succeeded) 17 { 18 context.Result = new
          JsonResult("您沒有此操作權(quán)限") 19 { 20 StatusCode = (int)HttpStatusCode.Forbidden 21
          };22 } 23 } 24 }

            從這兒開始,我估計有人就要看不懂了,下邊解釋下。首先,PermissionFilter是個授權(quán)過濾器,它實現(xiàn)了IAsyncAuthorizationFilter,所以會作為過濾器管道第一道去執(zhí)行。構(gòu)造函數(shù)中有2個參數(shù),IAuthorizationService直接注入,PermissionRequirement則通過特性標(biāo)記外部設(shè)置。在OnAuthorizationAsync重寫方法中,調(diào)用IAuthorizationService.AuthorizeAsync方法去做具體授權(quán)校驗工作,經(jīng)此橋接,授權(quán)流程就轉(zhuǎn)到我們最開始定義的PermissionHandler去了。本身core源碼中,IAuthorizationService是在授權(quán)中間件中使用到的,這里我借用了。注意,一旦過濾器注入了服務(wù),那此過濾器便不再能夠直接以打標(biāo)記的形式貼在控制器或方法上了,那種形式必須所有參數(shù)都直接指定。core中給出的方案,是TypeFilter,涉及到服務(wù)注入的時候。

            那接下來,就是另一個重要對象了:
          /// <summary> /// 權(quán)限特性 /// </summary> public class PermissionAttribute :
          TypeFilterAttribute {public PermissionAttribute(params string[] codes) : base(
          typeof(PermissionFilter)) { Arguments = new[] { new
          PermissionRequirement(codes) }; } }
            至此,各組件定義完畢,那我們使用就類似下邊這樣:



          3、效果演示

            guokun用戶角色:



          ?

          ?

          ?

          ?

          ?網(wǎng)站管理員角色對應(yīng)權(quán)限:



          ?

          ?  可以看到,是沒有勾選刪除部門的,那我們用這個賬戶來刪除下部門:



          ?

          ?  狀態(tài)碼403,并提示無權(quán)操作,刪除動作已經(jīng)被攔截了。那我們把刪除權(quán)限賦予網(wǎng)站管理員:



          ?

          ?  接下來再來刪除:



          ?

          ?  可以看到,已經(jīng)刪除部門成功。

          3、總結(jié)


            以上便是本項目權(quán)限控制的實現(xiàn)。認(rèn)證&授權(quán)這塊兒,如果要做好,還是得把core的整套機(jī)制弄清楚,最好能把源碼過一遍,不然根本搞不清楚需要怎么擴(kuò)展,每個擴(kuò)展點(diǎn)在什么時機(jī)觸發(fā)及生效。

          友情鏈接
          ioDraw流程圖
          API參考文檔
          OK工具箱
          云服務(wù)器優(yōu)惠
          阿里云優(yōu)惠券
          騰訊云優(yōu)惠券
          京東云優(yōu)惠券
          站點(diǎn)信息
          問題反饋
          郵箱:[email protected]
          QQ群:637538335
          關(guān)注微信

                干鸡巴在线视频 | 亚洲欧美日韩成人高清在线一区 | 高清成人免费视频 | 三级黄色片在线观看 | 国产一级一级农村 |