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

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

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

背景

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

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

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

ece103324217a92d16189bddefd4fe33.png

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

資源描述的管理

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

資源描述

創建一個?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)){//把資源名稱跟操作名稱組裝成PolicyPolicy = _resouceName + "-" + value;}}}
}

獲得所有資源

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

創建一個?ResourceInfoModelProvider?繼承?IApplicationModelProvider,其執行順序我們設置為=> -989。其執行順序:

?首先 (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);}
}

授權控制的實現

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

動態添加自定義授權策略

關于自定義授權策略提供程序[5]的說明,這里不再贅述微軟的文檔,里面已經介紹了很詳細,這里我們通過其特性可以動態的創建自定義授權策略,在訪問資源時我們獲取到剛剛標識的?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));
}

授權處理程序

前面我們已經可以動態創建授權的策略,那么關于授權策略的處理[6]我們可以實現?AuthorizationHandler?根據傳遞的策略處理要求對本次請求進行權限的分析。

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">檢查的依據</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)));}
}

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

最后

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

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

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

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

本文介紹的相關代碼已經提供 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

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/281661.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/281661.shtml
英文地址,請注明出處:http://en.pswp.cn/news/281661.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

數據歸一化

數據歸一化 數據的標準化是將數據按比例縮放&#xff0c;使之落入一個小的特定區間&#xff0c;一般為0到1之間。在某些比較和評價的指標處理中經常會用到&#xff0c;去除數據的單位限制&#xff0c;將其轉化為無量綱的純數值&#xff0c;便于不同單位或量級的指標能夠進行比較…

vi is failed with error E382: Cannot write, 'buftype' option is set in Linux

在linux下生成jar文件遇到了編碼問題&#xff0c;于是想vi t.jar&#xff0c;在保存是報錯&#xff1a;E382: Cannot write, buftype option is set 解決方法&#xff1a; 可以用下面的命名查看buftype的設置&#xff0c;當buftypenofile時&#xff0c;不能保存文件&#xff0c…

列表生成式的使用

輸入&#xff1a;[Hello, World, 18, Apple, None] 輸出&#xff1a;[hello, world, apple] L [Hello, World, 18, Apple, None] print([w.lower() for w in L if isinstance(w, str)])# -- coding: utf-8 -- L [Hello, World, 18, Apple, None] L2 [] L2 [w.lower() for w…

matlab 12位 顯示不出來,求助大神,為何不同機器運行MATLAB結果不同

求助&#xff1a;不同機器運行MATLAB結果不同我調用MATLAB優化工具箱的庫函數fmincon&#xff0c;使用相同的初始解(可行解)&#xff0c;對同一個問題進行局部搜索(算法為序列二次規劃&#xff0c;即SQP)&#xff0c;但在不同機器上得到的結果不同。一共有五臺機器 (為了方便&a…

.NET性能系列文章一:.NET7的性能改進

這些方法在.NET7 中變得更快照片來自 CHUTTERSNAP[1] 的 Unsplash[2]歡迎閱讀.NET 性能系列的第一章。這一系列的特點是對.NET 世界中許多不同的主題進行研究、比較性能。正如標題所說的那樣&#xff0c;本章節在于.NET7 中的性能改進。你將看到哪種方法是實現特定功能最快的方…

UVA - 10061 How many zero#39;s and how many digits ?

n!x*b^y, 當x為正整數時,最大的y就是n!末尾0的個數了, 把n,b分別拆成素因子相乘的形式: 比如, n5,b16 n5,b2^4, 非常明顯,末尾0的個數為0 10進制時,n!a*10^x b進制時,n!c*b^y 非常明顯,n!的位數就是最大的x1 這里計算我用了log,精度設置為1e-9 #include<iostream> #inclu…

丁洪波 -- 不要“ 總是拿著微不足道的成就來騙自己”

都市快報實盤大賽25期&#xff1a;于海飛/丁洪波榮獲冠亞軍 七禾網 時間&#xff1a;2010-11-02 12:47:05 來源&#xff1a;期貨中國10月30日下午&#xff0c;2010年浙商期貨實盤大賽第三季度&#xff08;都市快報實盤大賽第25期&#xff09;頒獎典禮在天科大廈浙商期貨大會議室…

面試專題(Mysql及Mongodb)

2019獨角獸企業重金招聘Python工程師標準>>> mysql面試題 1. 各個數據庫存儲引擎區別 mysql的存儲引擎是針對表進行設置的&#xff0c;一個庫的不同表可以設置不同的存儲引擎&#xff0c;mysql默認支持多種存儲引擎&#xff0c;以適用不同領域的數據庫應用需要&…

織夢網站翻頁php,dedecms織夢網站列表頁和內容頁分頁樣式

織夢分頁標簽{dede:pagelist istitem"index,pre,next,end,option,info," listsize"5"/}&#xff0c;{dede:prenext getpre/}&#xff0c;{dede:prenext getnext/}。默認樣式和使用模板css樣式布局不一樣,這時又不想重寫樣式&#xff0c;我們可以修改織夢標…

通過中間件添加用戶的Claim

本文主要介紹 Sang.AspNetCore.RoleBasedAuthorization[1] 庫如何通過中間件實現對用戶 Claim 的添加。背景前面我們介紹了通過對自定義授權策略和自定義授權處理程序的使用實現了基本的RBAC權限設計&#xff0c;將大量的用戶可訪問資源及操作的標識直接放到用戶的 JWT Token 中…

部署也是工程的一部分,也要編程(自動化)

部署和開發一樣&#xff0c;同樣面臨變化。同樣有復雜的細節。 同樣應該代碼化&#xff0c;自動化。把復雜性、思路&#xff0c;操作&#xff0c;都固化下來&#xff0c;顯式表達。 不要“雪花”式配置。 把最近看的文章摘抄一下 集句&#xff1a; 1頻繁做讓你感到痛苦的事情&a…

KDD走進阿里 數百專家聚集探討產學研一體化

6月29日&#xff0c;由阿里巴巴集團、中國中文信息學會、KDD China聯合主辦的數據挖掘前沿發展與未來論壇在杭州舉行&#xff0c;會議吸引了來自國際頂級高校和知名企業的近300名專家學者到場參會、近30000人在線觀看。論壇除了分享最新的數據挖掘領域最新科研成果及研發思路外…

zookeeper學習03 使用場景

zookeeper實際應用場景 zookeeper能夠實現哪些場景 1&#xff09;訂閱發布/配置中心 watcher機制 統一配置管理&#xff08;disconf&#xff09; 實現配置信息的集中式原理和數據的動態更新 實現配置中心有倆種模式&#xff1a;push,pull 長輪詢 zookeeper采用的是推拉相結合的…

php模板引擎循環start,PHP模板引擎Smarty內建函數section,sectionelse用法詳解

本文實例講述了PHP模板引擎Smarty內建函數section,sectionelse用法。分享給大家供大家參考&#xff0c;具體如下&#xff1a;section 是 Smarty 模板中除了 foreach 以外的另一種處理循環的方案&#xff0c;section 比 foreach 要靈活&#xff0c;就像是一個改進的 foreach 語句…

OpenHarmony操作系統與龍芯2K1000LA芯片完成適配,龍架構平臺獲得開源鴻蒙認證

近日&#xff0c;龍芯中科與軟通動力控股公司鴻湖萬聯共同完成OpenHarmony操作系統與龍芯2K1000LA處理器的適配&#xff0c;“乘風1000”開發板&#xff08;搭載龍芯2K1000LA&#xff09;榮獲OpenHarmony生態產品兼容性證書。至此&#xff0c;萬物互聯的OpenHarmony生態體系再次…

struts2開發action 的三種方法以及通配符、路徑匹配原則、常量

struts2開發action 的三種方法 1、繼承ActionSupport public class UserAction extends ActionSupport {// Action中業務處理方法public String login() {System.out.println("UserAction.login()"); // return "success";return SUCCESS;} } 2、實現…

閉包--閉包作用之保護(一)

閉包作用:保護 形成私有作用域,保護里面的私有變量不受外界干擾例如多人協作開發&#xff1a;A的代碼有fn(),B的代碼有fn(),但是他們不相互影響 // A的代碼<script>(function() {function fn1() {console.log("aa")}window.fn1 fn1;})()// window.fn1() //11&…

left join 和 inner join

2019獨角獸企業重金招聘Python工程師標準>>> left join 和 inner join 首先 MySQL 中 inner join 的效率確實要高于 left join。所以沒必要使用 left join 轉彎成 inner join 的效果。這樣不但效率降低&#xff0c;可讀性也會降低。 Number1 select from t1 left j…

oracle 數據庫中拆分,oracle數據庫字符串拆分

第一種 直接返回切分的字符串create or replace function Get_StrArrayLength(av_str varchar2,--要分割的字符串av_split varchar2 --分隔符號)return numberislv_str varchar2(1000);lv_length number;beginlv_str:ltrim(rtrim(av_str));lv_length:0;while instr(lv_str,av_s…

Vue3+.NET6,輕松開發管理后臺!(可復用)

在GitHub是沒找到簡單好用的Vue3.NET6管理后臺項目&#xff0c;有收藏的請評論區分享。這里分享一套Vue3 Axios TS Vite Element Plus .NET 6 WebAPI JWT SqlSugar的通用管理后臺&#xff0c;前后端分離架構&#xff0c;各種最新框架組件&#xff0c;實現了管理后臺幾乎…