日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > asp.net >内容正文

asp.net

Asp.Net Core 中IdentityServer4 实战之角色授权详解

發布時間:2023/12/4 asp.net 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Asp.Net Core 中IdentityServer4 实战之角色授权详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

前幾篇文章分享了IdentityServer4密碼模式的基本授權及自定義授權等方式,最近由于改造一個網關服務,也用到了IdentityServer4的授權,改造過程中發現比較適合基于Role角色的授權,通過不同的角色來限制用戶訪問不同的Api資源,這里我就來分享IdentityServer4基于角色的授權詳解。

IdentityServer4 歷史文章目錄

  • Asp.Net Core IdentityServer4 中的基本概念

  • Asp.Net Core 中IdentityServer4 授權中心之應用實戰

  • Asp.Net Core 中IdentityServer4 授權中心之自定義授權模式

  • Asp.Net Core 中IdentityServer4 授權原理及刷新Token的應用

  • Asp.Net Core 中IdentityServer4 實戰之 Claim詳解

沒有看過之前的幾篇文章,我建議先回過頭看看上面那幾篇文章再來看本篇文章,不過對于大牛來說就可以跳過了。。。。

二、模擬場景

還是按照我的文章風格套路,實戰之前先來模擬下應用場景,無場景的實戰都是耍流氓,模擬場景更能讓大家投入,同時也是自我學習、思考、總結的結晶之處!!!

對于角色授權大家也不陌生,大家比較熟悉的應該是RBAC的設計,這里就不闡述RBAC,有興趣的可以百度。我們這里簡單模擬下角色場景 假如有這么一個數據網關服務服務(下面我統稱為數據網關),客戶端有三種賬號角色(普通用戶、管理員用戶、超級管理員用戶),數據網關針對這三種角色用戶分配不同的數據訪問權限,場景圖如下:

那么這種場景我們會怎么去設計呢?這個場景還算比較簡單,角色比較單一,比較固定,對于這種場景很多人可能會考慮到通過Filter過濾器等方式來實現,這當然可以。不過針對這種場景IdentityServer4中本身就支持角色授權,下面我來給大家分享IdentityServer4的角色授權.

三、角色授權實戰

授權流程

擼代碼之前我們先整理下IdentityServer4的 角色授權流程圖,我簡單概括畫了下,流程圖如下:

場景圖概括如下:

  • 客戶端分為三種核心角色(普通用戶、管理員用戶、超級管理-老板)用戶,三種用戶訪問同一個數據網關(API資源)

  • 數據網關(API資源)對這三種用戶角色做了訪問限制。

角色授權流程解釋如下:

  • 第一步:不同的用戶攜帶用戶密碼等信息訪問授權中心(ids4)嘗試授權

  • 第二步:授權中心對用戶授權通過返回access_token給用戶同時聲明用戶的Role到Claim中。。

  • 第三步:客戶端攜帶拿到的access_token嘗試請求數據網關(API資源)。

  • 第四步:數據網關收到客戶端的第一次請求會到授權中心請求獲得驗證公鑰。

  • 第五步:授權中心返回驗證公鑰給數據網關并且緩存起來,后面不再到授權中心再次獲得驗證公鑰(只會請求一次,除非重啟服務)。

  • 第六步:數據網關(ids4)通過驗證網關驗證access_token是否驗證通過,并且驗證請求的客戶端用戶聲明的Role是否和請求的API資源約定的的角色一致。如果一致則通過第步返回給用戶端,否則直接拒絕請求.

擼代碼

代碼繼續上面幾篇文章的例子的續集,你懂的,就不從零開始擼代碼啦(強烈建議沒看過上面幾篇的先看下上面的目錄中的幾篇,要不然會一頭霧水,大佬跳過) 要使IdentityServer4實現的授權中心支持角色驗證的支持,我們需要在定義的API資源中添加角色的引入,代碼如下:上幾篇文章的授權中心(Jlion.NetCore.Identity.Service)的 代碼如下:

/// <summary>/// 資源/// </summary>/// <returns></returns>public static IEnumerable<ApiResource> GetApiResources(){return new List<ApiResource>{new ApiResource(OAuthConfig.UserApi.ApiName,OAuthConfig.UserApi.ApiName),};}

加入角色的支持代碼改造如下:

/// <summary>/// 資源/// </summary>/// <returns></returns>public static IEnumerable<ApiResource> GetApiResources(){return new List<ApiResource>{new ApiResource(OAuthConfig.UserApi.ApiName,OAuthConfig.UserApi.ApiName,new List<string>(){JwtClaimTypes.Role }),};}

API資源中添加了角色驗證的支持后,需要在用戶登錄授權成功后聲明Claim用戶的Role信息,代碼如下:改造前代碼:

public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator {public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context){try{var userName = context.UserName;var password = context.Password;//驗證用戶,這么可以到數據庫里面驗證用戶名和密碼是否正確var claimList = await ValidateUserAsync(userName, password);// 驗證賬號context.Result = new GrantValidationResult(subject: userName,authenticationMethod: "custom",claims: claimList.ToArray());}catch (Exception ex){//驗證異常結果context.Result = new GrantValidationResult(){IsError = true,Error = ex.Message};}}#region Private Method/// <summary>/// 驗證用戶/// </summary>/// <param name="loginName"></param>/// <param name="password"></param>/// <returns></returns>private async Task<List<Claim>> ValidateUserAsync(string loginName, string password){//TODO 這里可以通過用戶名和密碼到數據庫中去驗證是否存在,// 以及角色相關信息,我這里還是使用內存中已經存在的用戶和密碼var user = OAuthMemoryData.GetTestUsers();if (user == null)throw new Exception("登錄失敗,用戶名和密碼不正確");return new List<Claim>(){new Claim(ClaimTypes.Name, $"{loginName}"),new Claim(EnumUserClaim.DisplayName.ToString(),"測試用戶"),new Claim(EnumUserClaim.UserId.ToString(),"10001"),new Claim(EnumUserClaim.MerchantId.ToString(),"000100001"),};}#endregion}

為了保留之前文章的源代碼,好讓之前的文章源代碼可追溯,我這里不在源代碼上改造升級,我直接新增一個用戶密碼驗證器類, 命名為RoleTestResourceOwnerPasswordValidator,代碼改造如下:

/// <summary>/// 角色授權用戶名密碼驗證器demo/// </summary>public class RoleTestResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator{public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context){try{var userName = context.UserName;var password = context.Password;//驗證用戶,這么可以到數據庫里面驗證用戶名和密碼是否正確var claimList = await ValidateUserByRoleAsync(userName, password);// 驗證賬號context.Result = new GrantValidationResult(subject: userName,authenticationMethod: "custom",claims: claimList.ToArray());}catch (Exception ex){//驗證異常結果context.Result = new GrantValidationResult(){IsError = true,Error = ex.Message};}}#region Private Method/// <summary>/// 驗證用戶(角色Demo 專用方法)/// 這里和之前區分,主要是為了保留和博客同步源代碼/// </summary>/// <param name="loginName"></param>/// <param name="password"></param>/// <returns></returns>private async Task<List<Claim>> ValidateUserByRoleAsync(string loginName, string password){//TODO 這里可以通過用戶名和密碼到數據庫中去驗證是否存在,// 以及角色相關信息,我這里還是使用內存中已經存在的用戶和密碼var user = OAuthMemoryData.GetUserByUserName(loginName);if (user == null)throw new Exception("登錄失敗,用戶名和密碼不正確");//下面的Claim 聲明我為了演示,硬編碼了,//實際生產環境需要通過讀取數據庫的信息并且來聲明return new List<Claim>(){new Claim(ClaimTypes.Name, $"{user.UserName}"),new Claim(EnumUserClaim.DisplayName.ToString(),user.DisplayName),new Claim(EnumUserClaim.UserId.ToString(),user.UserId.ToString()),new Claim(EnumUserClaim.MerchantId.ToString(),user.MerchantId.ToString()),new Claim(JwtClaimTypes.Role.ToString(),user.Role.ToString())};}#endregion }

為了方便演示,我直接把Role定義成了一個公共枚舉EnumUserRole,代碼如下:

/// <summary> /// 角色枚舉 /// </summary> public enum EnumUserRole {Normal,Manage,SupperManage }

GetUserByUserName中硬編碼創建了三個角色的用戶,代碼如下:

/// <summary>/// 為了演示,硬編碼了,/// 這個方法可以通過DDD設計到底層數據庫去查詢數據庫/// </summary>/// <param name="userName"></param>/// <returns></returns>public static UserModel GetUserByUserName(string userName){var normalUser = new UserModel(){DisplayName = "張三",MerchantId = 10001,Password = "123456",Role = Enums.EnumUserRole.Normal,SubjectId = "1",UserId = 20001,UserName = "testNormal"};var manageUser = new UserModel(){DisplayName = "李四",MerchantId = 10001,Password = "123456",Role = Enums.EnumUserRole.Manage,SubjectId = "1",UserId = 20001,UserName = "testManage"};var supperManageUser = new UserModel(){DisplayName = "dotNET博士",MerchantId = 10001,Password = "123456",Role = Enums.EnumUserRole.SupperManage,SubjectId = "1",UserId = 20001,UserName = "testSupperManage"};var list = new List<UserModel>() {normalUser,manageUser,supperManageUser};return list?.Where(item => item.UserName.Equals(userName))?.FirstOrDefault();}

好了,現在用戶授權通過后聲明的Role也已經完成了,我上面使用的是JwtClaimTypes 默認支持的Role,你也可以不使用JwtClaimTypes類,可以自定義類來實現。最后為了讓新關注我的博客用戶沒看過之前幾篇文章的用戶不至于一頭霧水,我把注冊ids中間件代碼還是貼出來, 注冊新的用戶名密碼驗證器到DI中 代碼如下:

public void ConfigureServices(IServiceCollection services){services.AddControllers();#region 數據庫存儲方式services.AddIdentityServer().AddDeveloperSigningCredential().AddInMemoryApiResources(OAuthMemoryData.GetApiResources())//.AddInMemoryClients(OAuthMemoryData.GetClients()).AddClientStore<ClientStore>()//.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>().AddResourceOwnerValidator<RoleTestResourceOwnerPasswordValidator>().AddExtensionGrantValidator<WeiXinOpenGrantValidator>().AddProfileService<UserProfileService>();//添加微信端自定義方式的驗證#endregion}public void Configure(IApplicationBuilder app, IWebHostEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}//使用IdentityServer4 的中間件app.UseIdentityServer();app.UseRouting();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();}); }

授權中心的角色支持代碼擼完了,我們來改造上幾篇文章中說到的用戶網關服務,這里我就叫數據網關, 項目:Jlion.NetCore.Identity.UserApiService上一篇關于Asp.Net Core 中IdentityServer4 實戰之 Claim詳解文章中在數據網關服務中新增了UserController控制器,并添加了一個訪問用戶基本的Claim信息接口,之前的代碼如下:

[ApiController] [Route("[controller]")] public class UserController : ControllerBase {private readonly ILogger<UserController> _logger;public UserController(ILogger<UserController> logger){_logger = logger;}[Authorize][HttpGet]public async Task<object> Get(){var userId = User.UserId();return new{name = User.Name(),userId = userId,displayName = User.DisplayName(),merchantId = User.MerchantId(),};} }

上面的代碼中Authorize沒有指定Role,那相當于所有的用戶都可以訪問這個接口,接下來,我們在UserController中創建一個只能是超級管理員角色才能訪問的接口,代碼如下

[Authorize(Roles =nameof(EnumUserRole.SupperManage))][HttpGet("{id}")]public async Task<object> Get(int id){var userId = User.UserId();return new{name = User.Name(),userId = userId,displayName = User.DisplayName(),merchantId = User.MerchantId(),roleName=User.Role()//獲得當前登錄用戶的角色};}

到這里數據網關代碼也已經改造完了,我們接下來就是運行結果看看是否正確。

運行

我們分別通過命令行運行我們的授權網關服務和數據網關服務,分別如下圖:授權網關還是指定5000 端口,如下圖:數據網關跟之前幾篇文章一樣指定 5001 端口,如下圖:

現在授權網關和數據網關都已經完美運行起來了,接下來我們通過postman模擬請求。先來通過普通用戶(testNormal)請求授權中心獲得access_token,如下圖:請求驗證通過, 再來通過獲取到的access_token 獲取普通接口:也完美獲取到數據 再來訪問下標注了supperManage超級管理員的角色接口,如下圖:結果跟預想的一樣,返回了403訪問被拒絕,其他賬號運行也是一樣,我這里就不一一去運行訪問測試了,有興趣的同學可以到github 上拉起我的源代碼進行運行測試, 到這里基于ids4角色授權基礎應用也完成了。

結束語:上面分享學習了IdentityServer4?進行角色授權的實戰例子,但是從上面的例子中可以發現Controller或者Action中指定Role的使用場景不是很廣泛,對于固定的那種角色場景比較適用,但是對于一個龐大的系統來說,用戶的權限、角色和API資源是后臺靈活可以分配的,這種場景感覺就不是很合適,那IdentityServer4?有沒有什么好的方式實現呢?留給大家思考,思考就是思維的一大進步。

博客系列源代碼地址:https://github.com/a312586670/NetCoreDemo

感謝語:三月份即將過去,三月份同時也是美好的開始,我的博客從三月份開始整理分享,傳承著以一起學習,共同進步為目標,自我自律,開始分享相關技術。文章持續性同步至我的微信公眾號【dotNET博士】,這個月來初見成效,一個月內已經榮獲500+以上的粉絲,也感謝大家一直以來對我的關注,你的關注讓我更有動力分享更好的原創技術文章。還沒有關注微信公眾號的,搜索"dotNET博士"關注,或者微信掃下面的二維碼進行關注,同時大家也可以積極的分享或點個右下角的推薦,讓更多人的關注到我的文章。

總結

以上是生活随笔為你收集整理的Asp.Net Core 中IdentityServer4 实战之角色授权详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。