Asp.Net Core 中IdentityServer4 授權原理及重新整理Token的應用
阿新 • • 發佈:2020-03-19
## 一、前言
上面分享了`IdentityServer4` 兩篇系列文章,核心主題主要是`密碼授權模式`及`自定義授權模式`,但是僅僅是分享了這兩種模式的使用,這篇文章進一步來分享`IdentityServer4`的授權流程及`refreshtoken`。
系列文章目錄(**沒看過的先看這幾篇文章再來閱讀本文章**):
- [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)
為了繼續保持`IdentityServer4` 系列部落格分享上下文一致,我這裡再把上回`授權中心`拆分後的圖貼出來,如圖:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200315225158795-1178949567.png)
圖中的`授權中心`就是通過`IdentityServer4`實現的授權服務中心,我下面就直接用`授權中心`代替`IdentityServer4`的授權服務來繼續述說,也感謝大家對我的支援,一直閱讀我的文章。
## 二、授權流程
### 2.1 客戶端驗證流程圖
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200315224551855-1158236720.png)
流程圖中,客戶端僅僅會到`授權中心` 請求一次,並拿到驗證公鑰返回給`Api資源`擁有端,後面客戶端再次嘗試請求`Api資源`時候就不會再到`授權中心`去獲取驗證公鑰,會直接用之前獲取到的`公鑰`進行驗證,驗證通過則授權通過。
### 2.2 授權及重新整理refresh_token 流程圖
然而通過`授權中心` 獲取到的`access_token` 是有有效時間的,如果失效則需要通過`refresh_token` 重新到`授權中心`去重新整理獲取最新的`access_token`,整體的流程圖如下:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200316214849214-1093908921.jpg)
客戶端攜帶上一次獲取到的`access_token` 請求受保護的`Api資源`時,通過`公鑰`進行驗證時發現`access_token`已經過期,則客戶端再攜帶`refresh_token` 向`授權中心`再次發起請求,重新整理`access_token`以獲得最新的`access_token`和`refresh_token`,用最新的`access_token` 去獲取受保護的`Api資源`,這樣可以減少客戶端多次跳轉登入授權頁面,提高使用者體驗。
## 三、應用實戰
說到例子,我這裡不從零開始擼程式碼, 還是在之前的程式碼基礎上繼續改造程式碼,在原有的定義客戶端的程式碼中新增重新整理`access_token`的相關配置,程式碼如下:
```
public static IEnumerable GetClients()
{
return new List
{
new Client()
{
ClientId =OAuthConfig.UserApi.ClientId,
AllowedGrantTypes = new List()
{
GrantTypes.ResourceOwnerPassword.FirstOrDefault(),//Resource Owner Password模式
GrantTypeConstants.ResourceWeixinOpen,
},
ClientSecrets = {new Secret(OAuthConfig.UserApi.Secret.Sha256()) },
AllowOfflineAccess = true,//如果要獲取refresh_tokens ,必須把AllowOfflineAccess設定為true
AllowedScopes= {
OAuthConfig.UserApi.ApiName,
StandardScopes.OfflineAccess,
},
AccessTokenLifetime = OAuthConfig.ExpireIn,
},
};
}
```
如果你需要重新整理`access_token`,則需要把`AllowOfflineAccess`設定`true`,同時新增`StandardScopes.OfflineAccess` 這個`Scopes`,主要程式碼如下:
```
AllowOfflineAccess = true,//如果要獲取refresh_tokens ,必須把AllowOfflineAccess設定為true
AllowedScopes= {
OAuthConfig.UserApi.ApiName,
StandardScopes.OfflineAccess,//如果要獲取refresh_tokens ,必須在scopes中加上OfflineAccess
},
```
`授權中心`,完整程式碼如下:
`OAuthMemoryData` 程式碼如下:
```
///
///
///
public class OAuthMemoryData
{
///
/// 資源
///
///
public static IEnumerable GetApiResources()
{
return new List
{
new ApiResource(OAuthConfig.UserApi.ApiName,OAuthConfig.UserApi.ApiName),
};
}
public static IEnumerable GetClients()
{
return new List
{
new Client()
{
ClientId =OAuthConfig.UserApi.ClientId,
AllowedGrantTypes = new List()
{
GrantTypes.ResourceOwnerPassword.FirstOrDefault(),//Resource Owner Password模式
GrantTypeConstants.ResourceWeixinOpen,
},
ClientSecrets = {new Secret(OAuthConfig.UserApi.Secret.Sha256()) },
AllowOfflineAccess = true,//如果要獲取refresh_tokens ,必須把AllowOfflineAccess設定為true
AllowedScopes= {
OAuthConfig.UserApi.ApiName,
StandardScopes.OfflineAccess,
},
AccessTokenLifetime = OAuthConfig.ExpireIn,
},
};
}
///
/// 測試的賬號和密碼
///
///
public static List GetTestUsers()
{
return new List
{
new TestUser()
{
SubjectId = "1",
Username = "test",
Password = "123456"
},
};
}
///
/// 微信openId 的測試使用者
///
///
public static List GetWeiXinOpenIdTestUsers()
{
return new List
{
new TestUser(){
SubjectId="owerhwroogs3902openId",
}
};
}
}
```
`Startup` 完整程式碼如下:
```
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.Configure(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
#region 記憶體方式
//services.AddIdentityServer()
// .AddDeveloperSigningCredential()
// .AddInMemoryApiResources(OAuthMemoryData.GetApiResources())
// .AddInMemoryClients(OAuthMemoryData.GetClients())
// .AddTestUsers(OAuthMemoryData.GetTestUsers());
#endregion
#region 資料庫儲存方式
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryApiResources(OAuthMemoryData.GetApiResources())
//.AddInMemoryClients(OAuthMemoryData.GetClients())
.AddClientStore()
.AddResourceOwnerValidator()
.AddExtensionGrantValidator();//新增微信端自定義方式的驗證
#endregion
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
```
`授權中心`程式碼基本上已經改造完成,我們用postman 訪問`授權中心` 試一試,如下圖:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200316224158908-1620586984.png)
訪問結果中已經包含了`refresh_token`和`access_token`等相關資訊。
我們再來通過`access_token` 訪問`Api資源`(上兩篇有相關程式碼,未閱讀上兩篇先去查閱)這裡我就直接攜帶`access_token`去訪問,如圖:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200316225217253-1556797160.png)
訪問成功!!
我們再來重新整理下`refresh_token` ,訪問如圖:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200316225352851-1579605608.png)
重新整理`refresh_token`成功。
**我們到這裡再來做一個小小的測試,測試上面的授權流程中的,第4,5 步,上面說到第4步主要是客戶端第一次請求`Api資源`時會向`ids4`服務閘道器去請求獲取驗證公鑰,
獲取成功返回給`Api資源`並存儲在記憶體中,後續不再會到`ids4`服務去獲取驗證公鑰**
我們把上面的`授權中心` (ids4服務閘道器)停止執行,再來用之前的`access_token`請求`Api資源`,如下圖:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200316230013192-14329478.png)
現在已經確定`授權中心`(ids4服務閘道器)確實停止了,不能訪問了,那我們再來通過之前未過期的`access_token`來請求`Api資源`閘道器,結果如下圖:
![](https://img2020.cnblogs.com/blog/824291/202003/824291-20200316230121991-1846428283.png)
完美,請求還是成功,這完全證明:**客戶端請求Api資源閘道器(受保護的資源)時,第一次收到請求會到授權中心(ids4服務閘道器)獲取驗證公鑰,並保持到記憶體中,後面的請求不會再到授權中心去獲得驗證公鑰,而是Api資源閘道器(受保護的資源)中直接通過儲存下來的驗證公鑰進行驗證,從而通過授權**。