客戶端請求Api資源網關(受保護的資源)時,第一次收到請求會到授權中心(ids4服務網關)獲取驗證公鑰,並保持到記憶體中,後面的請求不會再到授權中心去獲得驗證公鑰,而是Api資源網關(受保護的資源)中直接通過保存下來的驗證公鑰進行驗證,從而通過授權 ...
一、前言
上面分享了IdentityServer4
兩篇系列文章,核心主題主要是密碼授權模式
及自定義授權模式
,但是僅僅是分享了這兩種模式的使用,這篇文章進一步來分享IdentityServer4
的授權流程及refreshtoken
。
系列文章目錄(沒看過的先看這幾篇文章再來閱讀本文章):
- Asp.Net Core IdentityServer4 中的基本概念
- Asp.Net Core 中IdentityServer4 授權中心之應用實戰
- Asp.Net Core 中IdentityServer4 授權中心之自定義授權模式
為了繼續保持IdentityServer4
系列博客分享上下文一致,我這裡再把上回授權中心
拆分後的圖貼出來,如圖:
圖中的授權中心
就是通過IdentityServer4
實現的授權服務中心,我下麵就直接用授權中心
代替IdentityServer4
的授權服務來繼續述說,也感謝大家對我的支持,一直閱讀我的文章。
二、授權流程
2.1 客戶端驗證流程圖
流程圖中,客戶端僅僅會到授權中心
請求一次,並拿到驗證公鑰返回給Api資源
擁有端,後面客戶端再次嘗試請求Api資源
時候就不會再到授權中心
去獲取驗證公鑰,會直接用之前獲取到的公鑰
進行驗證,驗證通過則授權通過。
2.2 授權及刷新refresh_token 流程圖
然而通過授權中心
獲取到的access_token
是有有效時間的,如果失效則需要通過refresh_token
重新到授權中心
去刷新獲取最新的access_token
,整體的流程圖如下:
客戶端攜帶上一次獲取到的access_token
請求受保護的Api資源
時,通過公鑰
進行驗證時發現access_token
已經過期,則客戶端再攜帶refresh_token
向授權中心
再次發起請求,刷新access_token
以獲得最新的access_token
和refresh_token
,用最新的access_token
去獲取受保護的Api資源
,這樣可以減少客戶端多次跳轉登錄授權頁面,提高用戶體驗。
三、應用實戰
說到例子,我這裡不從零開始擼代碼, 還是在之前的代碼基礎上繼續改造代碼,在原有的定義客戶端的代碼中新增刷新access_token
的相關配置,代碼如下:
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client()
{
ClientId =OAuthConfig.UserApi.ClientId,
AllowedGrantTypes = new List<string>()
{
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
代碼如下:
/// <summary>
///
/// </summary>
public class OAuthMemoryData
{
/// <summary>
/// 資源
/// </summary>
/// <returns></returns>
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource(OAuthConfig.UserApi.ApiName,OAuthConfig.UserApi.ApiName),
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client()
{
ClientId =OAuthConfig.UserApi.ClientId,
AllowedGrantTypes = new List<string>()
{
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,
},
};
}
/// <summary>
/// 測試的賬號和密碼
/// </summary>
/// <returns></returns>
public static List<TestUser> GetTestUsers()
{
return new List<TestUser>
{
new TestUser()
{
SubjectId = "1",
Username = "test",
Password = "123456"
},
};
}
/// <summary>
/// 微信openId 的測試用戶
/// </summary>
/// <returns></returns>
public static List<TestUser> GetWeiXinOpenIdTestUsers()
{
return new List<TestUser>
{
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<CookiePolicyOptions>(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<ClientStore>()
.AddResourceOwnerValidator<ResourceOwnerPasswordValidator>()
.AddExtensionGrantValidator<WeiXinOpenGrantValidator>();//添加微信端自定義方式的驗證
#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 訪問授權中心
試一試,如下圖:
訪問結果中已經包含了refresh_token
和access_token
等相關信息。
我們再來通過access_token
訪問Api資源
(上兩篇有相關代碼,未閱讀上兩篇先去查閱)這裡我就直接攜帶access_token
去訪問,如圖:
訪問成功!!
我們再來刷新下refresh_token
,訪問如圖:
刷新refresh_token
成功。
我們到這裡再來做一個小小的測試,測試上面的授權流程中的,第4,5 步,上面說到第4步主要是客戶端第一次請求Api資源
時會向ids4
服務網關去請求獲取驗證公鑰,
獲取成功返回給Api資源
並存儲在記憶體中,後續不再會到ids4
服務去獲取驗證公鑰
我們把上面的授權中心
(ids4服務網關)停止運行,再來用之前的access_token
請求Api資源
,如下圖:
現在已經確定授權中心
(ids4服務網關)確實停止了,不能訪問了,那我們再來通過之前未過期的access_token
來請求Api資源
網關,結果如下圖:
完美,請求還是成功,這完全證明:客戶端請求Api資源網關(受保護的資源)時,第一次收到請求會到授權中心(ids4服務網關)獲取驗證公鑰,並保持到記憶體中,後面的請求不會再到授權中心去獲得驗證公鑰,而是Api資源網關(受保護的資源)中直接通過保存下來的驗證公鑰進行驗證,從而通過授權。