第五節:IdentityServer4的Pkce機制、令牌刷新機制、混合授權模式


一. PKCE機制

1. 准備

(1). IDS4_Server1:認證授權服務器

(2). MvcClient1:web客戶端

 然后將上述兩個項目配置成授權碼模式(如何配置見上一節 IdentityServer4授權碼模式介紹和代碼實操演練

PS: PKCE機制是在授權碼模式的基礎上,增加了幾個驗證參數,使其更加安全。

2. 代碼配置

(1).IDS4_Server1中的Config1,新增 RequirePkce = true, 開啟Pkce授權校驗。

(2).MvcClient1中的ConfigureServices中, 新增options.UsePkce = true;開啟Pkce. (默認就是true,所以可以省略)

PS:實際上在上一節的授權碼模式中已經開啟了pkce,只是沒有單獨點明增加的參數的含義。

3. 剖析測試

(1).在導向認證服務器的請求和確認授權頁面的請求中,新增兩個參數:code_challenge 和 code_challenge_method.

(2).客戶端攜帶授權碼請求認證服務器的時候,攜帶的參數中新增: code_verifier

 

二. 令牌刷新機制

1. 准備

(1). IDS4_Server1:認證授權服務器

(2). MvcClient1:web客戶端

 然后將上述兩個項目配置成授權碼模式(如何配置見上一節 IdentityServer4授權碼模式介紹和代碼實操演練

2.代碼配置

(1).IDS4_Server1中的Config1,新增如下代碼:

(2).MvcClient1中的ConfigureServices中,新增如下代碼:

options.Scope.Add(OpenIdConnectScope.OfflineAccess);
options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(1);
options.TokenValidationParameters.RequireExpirationTime = true;

 

3.運行結果

頁面顯示多了個:  refresh_token

注意過期時間:Token.expires_at

 

 

 

三. 混合授權模式

1. 背景

  access token 不含有任何關於身份認證的信息(用戶聲明信息),access token 的生命期可能會非常的長,即使用戶離開了它仍有可能有效,它還有可能被用於無最終用戶參與的情況,還有一種情況就是 access token 可能會被其它的客戶端應用借用。所以,無論客戶端是如何得到的 access token, 它都無法從 access token 里得到最終用戶的信息以及最終用戶的身份認證狀態(用戶聲明信息)。

  在 OAuth2.0 里,access token 不是為客戶端准備的,它對於客戶端應該是不透明的, 但是客戶端也需要從 access token 得到一些用戶信息,實際上客戶端應用只是 access token 的展示者, access token

真正的目標觀眾是被保護的資源.

  在 OpenID Connect 里,加了一個 ID Token 令牌,它會和 access token 一同發送給客戶端用,用於識別當前用戶是他聲稱的的用戶.

2. 核心代碼

IDS4服務器:AllowedGrantTypes = GrantTypes.Hybrid

代碼分享:

 /// <summary>
    /// 混合模式
    /// </summary>
    public class Config2
    {
        /// <summary>
        /// IDS資源
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<IdentityResource> GetIds()
        {
            return new List<IdentityResource>
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            };
        }

        /// <summary>
        /// 可以使用ID4 Server 客戶端資源
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Client> GetClients()
        {
            List<Client> clients = new List<Client>() {
                new Client
                {
                    ClientId = "client1",
                    ClientSecrets = { new Secret("123456".Sha256()) },
                    //混合模式
                    AllowedGrantTypes = GrantTypes.Hybrid,
                    //需要確認授權
                    RequireConsent = true,
                    
                    //關閉PKCE校驗(默認是true)
                    RequirePkce = false,

                    //允許token通過瀏覽器
                    AllowAccessTokensViaBrowser=true,               
                    // where to redirect to after login(登錄)
                    RedirectUris = { "http://127.0.0.1:7072/signin-oidc" },
                    // where to redirect to after logout(退出)
                    PostLogoutRedirectUris = { "http://127.0.0.1:7072/signout-callback-oidc" },
                    //允許的范圍
                    AllowedScopes = new List<string>
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile
                    },
                    AlwaysIncludeUserClaimsInIdToken=true,
                      //允許脫機訪問
                    AllowOfflineAccess=true,
                    //accessToken有效期,默認事3600秒,即1小時 (單位:秒)
                    AccessTokenLifetime = 600

                }
            };
            return clients;
        }

        /// <summary>
        /// 定義可以使用ID4的用戶資源
        /// </summary>
        /// <returns></returns>
        public static List<TestUser> GetUsers()
        {
            var address = new
            {
                street_address = "One Hacker Way",
                locality = "Heidelberg",
                postal_code = 69118,
                country = "Germany"
            };
            return new List<TestUser>()
            {
                new TestUser
                {
                        SubjectId = "001",
                        Username = "ypf1",    //賬號
                        Password = "123456",  //密碼
                        Claims =
                        {
                            new Claim(JwtClaimTypes.Name, "Alice Smith"),
                            new Claim(JwtClaimTypes.GivenName, "Alice"),
                            new Claim(JwtClaimTypes.FamilyName, "Smith"),
                            new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"),
                            new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                            new Claim(JwtClaimTypes.WebSite, "http://alice.com"),
                            new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json)
                        }
                 },
                 new TestUser
                 {
                        SubjectId = "002",
                        Username = "ypf2",
                        Password = "123456",
                        Claims =
                        {
                            new Claim(JwtClaimTypes.Name, "Bob Smith"),
                            new Claim(JwtClaimTypes.GivenName, "Bob"),
                            new Claim(JwtClaimTypes.FamilyName, "Smith"),
                            new Claim(JwtClaimTypes.Email, "BobSmith@email.com"),
                            new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
                            new Claim(JwtClaimTypes.WebSite, "http://bob.com"),
                            //這是新的序列化模式哦
                            new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json)
                        }
                  }
            };
        }


    }
View Code

Mvc客戶端:options.ResponseType = OpenIdConnectResponseType.CodeIdToken

代碼分享:

            {
                JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
                //添加Cookie認證
                services.AddAuthentication(options =>
                {
                    options.DefaultScheme = "Cookies";
                    options.DefaultChallengeScheme = "oidc";
                })
                .AddCookie("Cookies")
                //通過OIDC協議遠程請求認證
                .AddOpenIdConnect("oidc", options =>
                {
                    options.Authority = "http://127.0.0.1:7070";   //認證授權服務器地址
                    options.RequireHttpsMetadata = false;
                    options.ClientId = "client1";    //客戶端ID
                    options.ClientSecret = "123456"; //客戶端秘鑰

                    //混合模式
                    options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
                    options.ResponseMode = OpenIdConnectResponseMode.FormPost;

                    options.SaveTokens = true;
                    //開啟token時間的校驗
                    options.Scope.Add(OpenIdConnectScope.OfflineAccess);
                    options.TokenValidationParameters.ClockSkew = TimeSpan.FromMinutes(1);
                    options.TokenValidationParameters.RequireExpirationTime = true;

                });
            }
View Code

特別注意:要關閉pkce的驗證,否則會報錯 code challenge required. 代碼:RequirePkce = false

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM