1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 7529字,需瀏覽 16分鐘

         ·

        2022-11-03 19:19

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

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

        背景

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

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

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

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

        資源描述的管理

        在開始權限驗證設計之前我們需要先對系統(tǒng)可訪問的資源進行標識和管理。在后面的權限分配時,我們通過標識好的資源進行資源和操作權限的分配。

        資源描述

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

        [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)]public class ResourceAttribute: AuthorizeAttribute, IAuthorizationRequirement{    private string _resouceName;    private string? _action;    /// <summary>    /// 設置資源類型    /// </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; } } }}

        獲得所有資源

        我們標識好系統(tǒng)中的資源后,還需要獲取到我們最終程序中都標識有哪些資源,這里就需使用 ASP.NET Core 中的應用程序模型[4]??梢栽诔绦騿訒r獲取到所有的 Controller 和 Controller 中的每一個方法,然后通過查詢 ResourceAttribute 將其統(tǒng)一存儲到靜態(tài)類中。

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

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

        其核心代碼如下:

        /// <summary>/// 基于其 Order 屬性以倒序調用/// </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 中的每個方法的特性 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); }}

        授權控制的實現(xiàn)

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

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

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

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

        授權處理程序

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

        internal class ResourceAuthorizationHandler : AuthorizationHandler<ResourceAttribute>{    /// <summary>    /// 授權處理    /// </summary>    /// <param name="context">請求上下文</param>    /// <param name="requirement">資源驗證要求</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) // 超級管理員權限,擁有 SangRBAC_Administrator 角色不檢查權限 || 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)) ); }}

        這里我們提供了一個內置固定角色名的超級管理員用戶,其請求不進行權限檢查。

        最后

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

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

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

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

        本文介紹的相關代碼已經提供 Nuget 包,并開源了代碼,感興趣的同學可以查閱:https://github.com/sangyuxiaowu/Sang.AspNetCore.RoleBasedAuthorization

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

        References

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


        瀏覽 63
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            青娱乐自拍偷拍视频 | 台湾a级艳片茶山春色 | 久久精品久久久久久久 | 亚洲国产91在线 | 少妇双乳好大有奶水 | 乌克兰性爱视频 | 影音先锋 麻豆 | 肏屄片网站 | 亚洲国产一区二区三区 | 国产成人 亚洲 在线电影 |