上面分享了IdentityServer4
兩篇系列文章,核心主題主要是密碼受權模式
及自定義受權模式
,可是僅僅是分享了這兩種模式的使用,這篇文章進一步來分享IdentityServer4
的受權流程及refreshtoken
。html
系列文章目錄(沒看過的先看這幾篇文章再來閱讀本文章):數據庫
爲了繼續保持IdentityServer4
系列博客分享上下文一致,我這裏再把上回受權中心
拆分後的圖貼出來,如圖:
微信
圖中的受權中心
就是經過IdentityServer4
實現的受權服務中心,我下面就直接用受權中心
代替IdentityServer4
的受權服務來繼續述說,也感謝你們對個人支持,一直閱讀個人文章。cookie
流程圖中,客戶端僅僅會到受權中心
請求一次,並拿到驗證公鑰返回給Api資源
擁有端,後面客戶端再次嘗試請求Api資源
時候就不會再到受權中心
去獲取驗證公鑰,會直接用以前獲取到的公鑰
進行驗證,驗證經過則受權經過。app
然而經過受權中心
獲取到的access_token
是有有效時間的,若是失效則須要經過refresh_token
從新到受權中心
去刷新獲取最新的access_token
,總體的流程圖以下:
post
客戶端攜帶上一次獲取到的access_token
請求受保護的Api資源
時,經過公鑰
進行驗證時發現access_token
已通過期,則客戶端再攜帶refresh_token
向受權中心
再次發起請求,刷新access_token
以得到最新的access_token
和refresh_token
,用最新的access_token
去獲取受保護的Api資源
,這樣能夠減小客戶端屢次跳轉登陸受權頁面,提升用戶體驗。測試
說到例子,我這裏不從零開始擼代碼, 仍是在以前的代碼基礎上繼續改造代碼,在原有的定義客戶端的代碼中新增刷新access_token
的相關配置,代碼以下:ui
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
,主要代碼以下:this
AllowOfflineAccess = true,//若是要獲取refresh_tokens ,必須把AllowOfflineAccess設置爲true AllowedScopes= { OAuthConfig.UserApi.ApiName, StandardScopes.OfflineAccess,//若是要獲取refresh_tokens ,必須在scopes中加上OfflineAccess },
受權中心
,完整代碼以下:3d
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資源網關(受保護的資源)中直接經過保存下來的驗證公鑰進行驗證,從而經過受權。