<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用.NET從零實(shí)現(xiàn)基于用戶角色的訪問權(quán)限控制

          共 7529字,需瀏覽 16分鐘

           ·

          2022-11-03 19:19

          使用.NET從零實(shí)現(xiàn)基于用戶角色的訪問權(quán)限控制

          本文將介紹如何實(shí)現(xiàn)一個(gè)基于.NET RBAC 權(quán)限管理系統(tǒng),如果您不想了解原理,可查看推送的另一篇文章關(guān)于Sang.AspNetCore.RoleBasedAuthorization[1] 庫是使用介紹,直接使用該庫即可。

          背景

          在設(shè)計(jì)系統(tǒng)時(shí),我們必然要考慮系統(tǒng)使用的用戶,不同的用戶擁有不同的權(quán)限。主流的權(quán)限管理系統(tǒng)都是RBAC模型(Role-Based Access Control 基于角色的訪問控制)的變形和運(yùn)用,只是根據(jù)不同的業(yè)務(wù)和設(shè)計(jì)方案,呈現(xiàn)不同的顯示效果。

          在微軟文檔中我們了解了《基于角色的授權(quán)》[2],但是這種方式在代碼設(shè)計(jì)之初,就設(shè)計(jì)好了系統(tǒng)角色有什么,每個(gè)角色都可以訪問哪些資源。針對簡單的或者說變動不大的系統(tǒng)來說這些完全是夠用的,但是失去了靈活性。因?yàn)槲覀儾荒茏杂傻膭?chuàng)建新的角色,為其重新指定一個(gè)新的權(quán)限范圍,畢竟就算為用戶賦予多個(gè)角色,也會出現(xiàn)重疊或者多余的部分。

          RBAC(Role-Based Access Control)即:基于角色的權(quán)限控制。通過角色關(guān)聯(lián)用戶,角色關(guān)聯(lián)權(quán)限的方式間接賦予用戶權(quán)限。

          RBAC模型可以分為:RBAC0、RBAC1、RBAC2、RBAC3 四種。其中RBAC0是基礎(chǔ),也是最簡單的,今天我們就先從基礎(chǔ)的開始。

          資源描述的管理

          在開始權(quán)限驗(yàn)證設(shè)計(jì)之前我們需要先對系統(tǒng)可訪問的資源進(jìn)行標(biāo)識和管理。在后面的權(quán)限分配時(shí),我們通過標(biāo)識好的資源進(jìn)行資源和操作權(quán)限的分配。

          資源描述

          創(chuàng)建一個(gè) ResourceAttribute 繼承 AuthorizeAttribute 和 IAuthorizationRequirement 資源描述屬性,描述訪問的角色需要的資源要求。通過轉(zhuǎn)化為 Policy 來對 策略的授權(quán)[3] 提出要求。

          [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]public class ResourceAttribute: AuthorizeAttribute, IAuthorizationRequirement{    private string _resouceName;    private string? _action;    /// <summary>    /// 設(shè)置資源類型    /// </summary>    /// <param name="name">資源名稱</param>    /// <exception cref="ArgumentNullException">資源名稱不能為空</exception>    public ResourceAttribute(string name)    {        if (string.IsNullOrEmpty(name))        {            throw new ArgumentNullException(nameof(name));        }        string[] resourceValues = name.Split('-');        _resouceName = resourceValues[0];        if (resourceValues.Length > 1)        {            Action = resourceValues[1];        }        else        {            Policy = resourceValues[0];        }    }
          /// <summary> /// 獲取資源名稱 /// </summary> /// <returns></returns> public string GetResource() { return _resouceName; }
          /// <summary> /// 獲取操作名稱 /// </summary> public string? Action { get { return _action; } set { _action = value; if (!string.IsNullOrEmpty(value)) { //把資源名稱跟操作名稱組裝成Policy Policy = _resouceName + "-" + value; } } }}

          獲得所有資源

          我們標(biāo)識好系統(tǒng)中的資源后,還需要獲取到我們最終程序中都標(biāo)識有哪些資源,這里就需使用 ASP.NET Core 中的應(yīng)用程序模型[4]。可以在程序啟動時(shí)獲取到所有的 Controller 和 Controller 中的每一個(gè)方法,然后通過查詢 ResourceAttribute 將其統(tǒng)一存儲到靜態(tài)類中。

          創(chuàng)建一個(gè) ResourceInfoModelProvider 繼承 IApplicationModelProvider,其執(zhí)行順序我們設(shè)置為=> -989。其執(zhí)行順序:

          ?首先 (Order=-1000):DefaultApplicationModelProvider?然后(Order= -990):AuthorizationApplicationModelProvider CorsApplicationModelProvider?接著是這個(gè) ResourceInfoModelProvider

          其核心代碼如下:

          /// <summary>/// 基于其 Order 屬性以倒序調(diào)用/// </summary>/// <param name="context"></param>public void OnProvidersExecuted(ApplicationModelProviderContext context){    if (context == null)    {        throw new ArgumentNullException(nameof(context));    }    //獲取所有的控制器    List<ResourceAttribute> attributeData = new List<ResourceAttribute>();    foreach (var controllerModel in context.Result.Controllers)    {        //得到ResourceAttribute
          //Controller 的特性 var resourceData = controllerModel.Attributes.OfType<ResourceAttribute>().ToArray(); if (resourceData.Length > 0) { attributeData.AddRange(resourceData); } //Controller 中的每個(gè)方法的特性 foreach (var actionModel in controllerModel.Actions) { var actionResourceData = actionModel.Attributes.OfType<ResourceAttribute>().ToArray(); if (actionResourceData.Length > 0) { attributeData.AddRange(actionResourceData); } } } // 整理信息集中存入全局 foreach (var item in attributeData) { ResourceData.AddResource(item.GetResource(), item.Action); }}

          授權(quán)控制的實(shí)現(xiàn)

          接下來我們要對授權(quán)控制來進(jìn)行編碼實(shí)現(xiàn),包含自定義授權(quán)策略的實(shí)現(xiàn)和自定義授權(quán)處理程序。

          動態(tài)添加自定義授權(quán)策略

          關(guān)于自定義授權(quán)策略提供程序[5]的說明,這里不再贅述微軟的文檔,里面已經(jīng)介紹了很詳細(xì),這里我們通過其特性可以動態(tài)的創(chuàng)建自定義授權(quán)策略,在訪問資源時(shí)我們獲取到剛剛標(biāo)識的 Policy 沒有處理策略,就直接新建一個(gè),并傳遞這個(gè)策略的權(quán)限檢查信息,當(dāng)然這只是一方面,更多妙用,閱讀文檔里面其適用范圍的說明即可。

          /// <summary>/// 自定義授權(quán)策略/// 自動增加 Policy 授權(quán)策略/// </summary>/// <param name="policyName">授權(quán)名稱</param>/// <returns></returns>public Task<AuthorizationPolicy> GetPolicyAsync(string policyName){    // 檢查這個(gè)授權(quán)策略有沒有    AuthorizationPolicy? policy = _options.GetPolicy(policyName);
          if (policy is null) { _options.AddPolicy(policyName, builder => { builder.AddRequirements(new ResourceAttribute(policyName)); }); }
          return Task.FromResult(_options.GetPolicy(policyName));}

          授權(quán)處理程序

          前面我們已經(jīng)可以動態(tài)創(chuàng)建授權(quán)的策略,那么關(guān)于授權(quán)策略的處理[6]我們可以實(shí)現(xiàn) AuthorizationHandler 根據(jù)傳遞的策略處理要求對本次請求進(jìn)行權(quán)限的分析。

          internal class ResourceAuthorizationHandler : AuthorizationHandler<ResourceAttribute>{    /// <summary>    /// 授權(quán)處理    /// </summary>    /// <param name="context">請求上下文</param>    /// <param name="requirement">資源驗(yàn)證要求</param>    /// <returns></returns>    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ResourceAttribute requirement)    {        // 需要有用戶        if (context.User is null) return Task.CompletedTask;

          if (context.User.IsInRole(ResourceRole.Administrator) // 超級管理員權(quán)限,擁有 SangRBAC_Administrator 角色不檢查權(quán)限 || CheckClaims(context.User.Claims, requirement) // 符合 Resource 或 Resource-Action 組合的 Permission ) { context.Succeed(requirement); } return Task.CompletedTask;
          }
          /// <summary> /// 檢查 Claims 是否符合要求 /// </summary> /// <param name="claims">待檢查的claims</param> /// <param name="requirement">檢查的依據(jù)</param> /// <returns></returns> private bool CheckClaims(IEnumerable<Claim> claims, ResourceAttribute requirement) { return claims.Any(c => string.Equals(c.Type, ResourceClaimTypes.Permission, StringComparison.OrdinalIgnoreCase) && (string.Equals(c.Value, requirement.GetResource(), StringComparison.Ordinal) || string.Equals(c.Value, $"{requirement.GetResource()}-{requirement.Action}", StringComparison.Ordinal)) ); }}

          這里我們提供了一個(gè)內(nèi)置固定角色名的超級管理員用戶,其請求不進(jìn)行權(quán)限檢查。

          最后

          這里我們已經(jīng)實(shí)現(xiàn)了簡單的 RBAC 權(quán)限設(shè)計(jì),之后我們主要在生成 JWT 時(shí)帶上可訪問資源的Permission即可。

          new Claim(ResourceClaimTypes.Permission,"查詢")

          當(dāng)然,如果直接放在 jwt 中會讓 Token 變得很長,雖然我其實(shí)并不理解微軟的 ClaimTypes 使用一個(gè)URI標(biāo)識,如果有了解的朋友可以幫我解個(gè)惑,萬分感謝 https://stackoverflow.com/questions/72293184/ 。

          回到這個(gè)問題,我們可以再設(shè)計(jì)一個(gè)中間件,在獲取到用戶的角色名時(shí)將其關(guān)于角色權(quán)限的ClaimTypes加入到 content.User 即可。關(guān)于這一方面的詳細(xì)介紹和實(shí)現(xiàn)可以看下一篇文章。

          本文介紹的相關(guān)代碼已經(jīng)提供 Nuget 包,并開源了代碼,感興趣的同學(xué)可以查閱:https://github.com/sangyuxiaowu/Sang.AspNetCore.RoleBasedAuthorization

          如有錯漏之處,敬請指正。

          References

          [1] Sang.AspNetCore.RoleBasedAuthorization: https://www.nuget.org/packages/Sang.AspNetCore.RoleBasedAuthorization
          [2] 《基于角色的授權(quán)》: https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/roles?view=aspnetcore-6.0
          [3] 策略的授權(quán): https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/policies?view=aspnetcore-6.0
          [4] 使用 ASP.NET Core 中的應(yīng)用程序模型: https://learn.microsoft.com/zh-cn/aspnet/core/mvc/controllers/application-model?view=aspnetcore-6.0
          [5] 自定義授權(quán)策略提供程序: https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/iauthorizationpolicyprovider?view=aspnetcore-6.0
          [6] 授權(quán)策略的處理: https://learn.microsoft.com/zh-cn/aspnet/core/security/authorization/policies?view=aspnetcore-6.0


          瀏覽 63
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亲子伦视频一区二区 | 91在线观看视频 | 国产性情网站在线看 | 久久久久成人精品无码中文字幕 | 丁香五月婷婷在线观看 |