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

1、前言

上面分享了IdentityServer4 兩篇系列文章,核心主題主要是密碼受權模式自定義受權模式,可是僅僅是分享了這兩種模式的使用,這篇文章進一步來分享IdentityServer4的受權流程及refreshtokenhtml

系列文章目錄(沒看過的先看這幾篇文章再來閱讀本文章):數據庫

爲了繼續保持IdentityServer4 系列博客分享上下文一致,我這裏再把上回受權中心拆分後的圖貼出來,如圖:
微信

圖中的受權中心就是經過IdentityServer4實現的受權服務中心,我下面就直接用受權中心代替IdentityServer4的受權服務來繼續述說,也感謝你們對個人支持,一直閱讀個人文章。cookie

2、受權流程

2.1 客戶端驗證流程圖

流程圖中,客戶端僅僅會到受權中心 請求一次,並拿到驗證公鑰返回給Api資源擁有端,後面客戶端再次嘗試請求Api資源時候就不會再到受權中心去獲取驗證公鑰,會直接用以前獲取到的公鑰進行驗證,驗證經過則受權經過。app

2.2 受權及刷新refresh_token 流程圖

然而經過受權中心 獲取到的access_token 是有有效時間的,若是失效則須要經過refresh_token 從新到受權中心去刷新獲取最新的access_token,總體的流程圖以下:
post

客戶端攜帶上一次獲取到的access_token 請求受保護的Api資源時,經過公鑰進行驗證時發現access_token已通過期,則客戶端再攜帶refresh_token受權中心再次發起請求,刷新access_token以得到最新的access_tokenrefresh_token,用最新的access_token 去獲取受保護的Api資源,這樣能夠減小客戶端屢次跳轉登陸受權頁面,提升用戶體驗。測試

3、應用實戰

說到例子,我這裏不從零開始擼代碼, 仍是在以前的代碼基礎上繼續改造代碼,在原有的定義客戶端的代碼中新增刷新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_tokenaccess_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資源網關(受保護的資源)中直接經過保存下來的驗證公鑰進行驗證,從而經過受權

相關文章
相關標籤/搜索