Asp.Net Core 中IdentityServer4 實戰之角色授權詳解
阿新 • • 發佈:2020-03-30
## 一、前言
前幾篇文章分享了`IdentityServer4`密碼模式的基本授權及自定義授權等方式,最近由於改造一個閘道器服務,用到了`IdentityServer4`的授權,改造過程中發現比較適合基於`Role`角色的授權,通過不同的角色來限制使用者訪問不同的`Api資源`,這裡我就來分享`IdentityServer4`基於角色的授權詳解。
#### IdentityServer4 歷史文章目錄
- [Asp.Net Core IdentityServer4 中的基本概念](https://www.cnblogs.com/jlion/p/12437441.html)
- [Asp.Net Core 中IdentityServer4 授權中心之應用實戰](https://www.cnblogs.com/jlion/p/12447081.html)
- [Asp.Net Core 中IdentityServer4 授權中心之自定義授權模式](https://www.cnblogs.com/jlion/p/12468365.html)
- [Asp.Net Core 中IdentityServer4 授權原理及重新整理Token的應用](https://www.cnblogs.com/jlion/p/12501195.html)
- [Asp.Net Core 中IdentityServer4 實戰之 Claim詳解](https:////www.cnblogs.com/jlion/p/12543486.html)
沒有看過之前的幾篇文章,我建議先回過頭看看上面那幾篇文章再來看本篇文章,不過對於大牛來說就可以跳過了。。。。
## 二、模擬場景
還是按照我的文章風格套路,實戰之前先來模擬下應用場景,無場景的實戰都是耍流氓,模擬場景更能讓大家投入,同時也是自我學習、思考、總結的結晶之處!!!
對於角色授權大家也不陌生,大家比較熟悉的應該是`RBAC`的設計,這裡就不闡述`RBAC`,有興趣的可以百度。我們這裡簡單模擬下角色場景
假如有這麼一個`資料閘道器服務`服務(下面我統稱為`資料閘道器`),客戶端有三種賬號角色(普通使用者、管理員使用者、超級管理員使用者),資料閘道器針對這三種角色使用者分配不同的資料訪問許可權,場景圖如下:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200328175216974-1045194802.jpg)
那麼這種場景我們會怎麼去設計呢?這個場景還算比較簡單,角色比較單一,比較固定,對於這種場景很多人可能會考慮到通過`Filter`過濾器等方式來實現,這當然可以。不過正對這種場景`IdentityServer4`中本身就支援角色授權,下面我來給大家分享`IdentityServer4`的角色授權.
## 三、角色授權實戰
#### 授權流程
擼程式碼之前我們先整理下`IdentityServer4`的 角色授權流程圖,我簡單概括畫了下,流程圖如下:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200328175347833-226144972.jpg)
場景圖概括如下:
- 客戶端分為三種核心角色(普通使用者、管理員使用者、超級管理-老闆)使用者,三種使用者訪問同一個`資料閘道器`(API資源)
- `資料閘道器`(API資源)對這三種使用者角色做了訪問限制。
角色授權流程解釋如下:
- 第一步: 不同的使用者攜帶使用者密碼等資訊訪問`授權中心`(ids4)嘗試授權
- 第二步: `授權中心`對使用者授權通過返回`access_token`給使用者同時宣告使用者的`Role`到`Claim`中。。
- 第三步: 客戶端攜帶拿到的`access_token`嘗試請求`資料閘道器`(API資源)。
- 第四步:`資料閘道器`收到客戶端的第一次請求會到`授權中心`請求獲得驗證公鑰。
- 第五步:`授權中心`返回`驗證公鑰`給`資料閘道器`並且快取起來,後面不再到`授權中心`再次獲得驗證公鑰(只會請求一次,除非重啟服務)。
- 第六步:`資料閘道器`(ids4)通過驗證閘道器驗證`access_token`是否驗證通過,並且驗證請求的客戶端使用者宣告的`Role`是否和請求的`API資源`約定的的角色一致。如果一致則通過第步返回給使用者端,否則直接拒絕請求.
#### 擼程式碼
程式碼繼續上面幾篇文章的例子的續集,你懂的,就不從零開始擼程式碼啦(強烈建議沒看過上面幾篇的先看下上面的目錄中的幾篇,要不然會一頭霧水,大佬跳過)
要使`IdentityServer4`實現的`授權中心`支援角色驗證的支援,我們需要在定義的`API資源`中新增`角色`的引入,程式碼如下:
上幾篇文章的`授權中心`(Jlion.NetCore.Identity.Service)的
程式碼如下:
```
///
/// 資源
///
///
public static IEnumerable GetApiResources()
{
return new List
{
new ApiResource(OAuthConfig.UserApi.ApiName,OAuthConfig.UserApi.ApiName),
};
}
```
加入角色的支援程式碼改造如下:
```
///
/// 資源
///
///
public static IEnumerable GetApiResources()
{
return new List
{
new ApiResource(
OAuthConfig.UserApi.ApiName,
OAuthConfig.UserApi.ApiName,
new List(){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
///
/// 驗證使用者
///
///
///
///
private async Task
- > ValidateUserAsync(string loginName, string password)
{
//TODO 這裡可以通過使用者名稱和密碼到資料庫中去驗證是否存在,
// 以及角色相關資訊,我這裡還是使用記憶體中已經存在的使用者和密碼
var user = OAuthMemoryData.GetTestUsers();
if (user == null)
throw new Exception("登入失敗,使用者名稱和密碼不正確");
return new List
- > ValidateUserByRoleAsync(string loginName, string password)
{
//TODO 這裡可以通過使用者名稱和密碼到資料庫中去驗證是否存在,
// 以及角色相關資訊,我這裡還是使用記憶體中已經存在的使用者和密碼
var user = OAuthMemoryData.GetUserByUserName(loginName);
if (user == null)
throw new Exception("登入失敗,使用者名稱和密碼不正確");
//下面的Claim 宣告我為了演示,硬編碼了,
//實際生產環境需要通過讀取資料庫的資訊並且來宣告
return new List