IdentityServer4中的核心類


啟動配置器IIdentityServerBuilder

可以把它理解為一個IServiceCollection的容器,它商品有幾個擴展方法,方便我們用來注冊ids使用到的相關服務,為啥不直接擴展IServiceCollection而是包一層呢?因為這樣封裝性更好,與ids相關服務注冊方法都在IIdentityServerBuilder,不至於讓IServiceCollection點出方法的時候太亂

ids相關服務注冊體現在核心的擴展方法AddIdentityServer()上,主要任務如下:

AddRequiredPlatformServices:注冊基礎服務,不必深究

AddCookieAuthentication:單點登陸時用用到cookie身份驗證方案

AddCoreServices:注冊ids需要的核心的服務

AddDefaultEndpoints:ids重要的終結點都是在這里注冊的

AddPluggableServices:注冊插件服務

AddValidators:注冊各種驗證器

AddResponseGenerators:注冊各種token構建器

AddDefaultSecretParsers:

AddDefaultSecretValidators

AddInMemoryPersistedGrants:

ids配置選項IdentityServerOptions

使用的.net core的選項模式,它包含基本的配置項,也包含其它服務關聯的選項對象,因此我們可以在啟動配置中通過這些選項來定制ids的某些功能

驗證過的請求ValidatedRequest

它表示客戶端向ids發起的一個請求,且這個請求是驗證通過了的。,但它有時候只是一個初步的驗證,比如在登陸獲取code時它就沒有驗證客戶端密鑰。關於何時?如何?驗證的在后面會說,先看看它的成員來感受下它

  • Raw:請求的原始參數,NameValueCollection
  • ClientId:客戶端id
  • Client:驗證時根據當前請求的client_id參數在配置中獲取的客戶端實體
  • Secret:它表示客戶端的密鑰,在ids4網頁登陸在獲取code的步驟中是不需要密鑰的,ids只要能識別是哪各客戶端就行,只有在后續獲取AccessToken時才需要密鑰
  • AccessTokenLifetime:在ids配置客戶端時可以設置AccessToken的有效時長,在發放accessToken前也可以修改這個值
  • ClientClaims:同上,若是用戶使用此客戶端登陸時在需要在accessToken中包含哪些cliaim,也是在ids中配置客戶端設置的,當然在發放accessToken前也可以改。資源服務器可以根據解析得到的Claim進行更靈活的權限檢查
  • AccessTokenType:accessToken分為引用token和jwttoken,這個也是在ids配置客戶端時配置的,可以在發放accessToken前修改
  • Subject:代表當前用戶
  • SessionId:一次授權會產生一個唯一id,idtoken和accessToken的claim中都會保存這個sessionid
  • Options:這個是ids4在啟動時配置的選項對象的引用
  • ValidatedResources:本次請求且驗證同的資源的列表
  • Confirmation:客戶端密鑰驗證時產生的一個值
  • SetClient(Client client, ParsedSecret secret = null, string confirmation = ""):來設置客戶端,從查找引用可以看出AuthorizeRequestValidator.ValidateAsync時沒有設置驗證后的密鑰

驗證過的授權請求ValidatedAuthorizeRequest

它表示客戶端向ids發起的一個驗證過的授權請求,它繼承ValidatedRequest。所以它也表示一個ids中驗證過的請求,只不過是一個發起授權的請求

  • ResponseType:本次授權請求希望返回的數據類型,可選值定義在OidcConstants.ResponseTypes常量中,有:Code、Token、IdToken、IdTokenToken、CodeIdToken、CodeToken、CodeIdTokenToken。使用驗證碼模式發起授權請求時響應類型就是Code
  • ResponseMode:集成ids4網頁登陸時,當向客戶端返回code時以什么樣的方式,默認是動態構建一個Form表單Post到客戶端的回調地址。還可以設置為QueryString方式。后續客戶端回用code再請求ids換取accessToken和idToken
  • GrantType:對於ids的4中授權模式,在GrantType常量中定義,AuthorizationCode、Implicit、Hybrid,客戶端憑據和資源所有者密碼模式中授權流程比較簡單,不需要ids服務器與客戶端多次交互,所以不需要這里授權碼模式
  • RedirectUri:當授權流程完成后需要跳轉回客戶端指定的地址
  • RequestedScopes:客戶端請求時指定的希望請求的scope列表
  • WasConsentShown:在用戶登陸時是否需要用戶對客戶端請求的scope進行確認
  • State:OAuth2中規定的state,客戶端發起請求時攜帶此參數,返回code時原樣攜帶回客戶端的回調地址,起到一個參數傳遞作用
  • IsOpenIdRequest:只有請求的scope中包含openid就為true,某些授權流程驗證時需要使用到此參數
  • IsApiResourceRequest:只有請求的scope中包含api資源的scope則為true
  • Nonce:OAuth2中規定的隨機數,會參與到token的簽名和驗證
  • AuthenticationContextReferenceClasses:客戶端請求時可以攜帶一個acr_values參數,值的格式為:idp:qq bb:sdf。acr是AuthenticationContextReferenceClasses的縮寫,idp是identityProvider的縮寫,表示本次請求希望使用什么身份驗證方式,可以是local,表示用ids的本地用戶登陸,也可以讓ids使用第三方登陸。acr值可以用空格分隔多個。
  • PromptModes:不太懂,提示模式,提示用戶登陸、提示確認授權范圍
  • LoginHint:客戶端跳轉到ids登錄頁時可以指定一個默認的登陸用戶名
  • CodeChallenge:參考:https://www.dazhuanlan.com/2019/12/24/5e01e8e3c136f/
  • CodeChallengeMethod:同上
  • RequestObjectValues:客戶端發起授權請求時通常以queryString形式傳遞參數,但oidc協議還規定了可以通過jwt格式傳遞參數,參考:https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests。RequestObjectValues就表示解析到的參數的值

授權請求驗證器AuthorizeRequestValidator

它表示對客戶端的授權請求的驗證器,主要在以下場景被使用到

  1. 在客戶端集成ids的網頁登陸時,當跳轉到ids的登陸頁面get請求以及錄入賬號密碼發起post請求做登陸時
  2. 在集成網頁登陸ids做完身份驗證后還會跳轉到自己的callback地址(終結點),也就是請求AuthorizeCallbackEndpoint
  3. 在不是集成ids網頁登陸,而是直接像請求AuthorizeEndpoint獲取臨時code時

其中1、2是集成網頁登陸時會經歷的兩個步驟,核心的驗證方法的簽名是這樣的

Task<AuthorizeRequestValidationResult> ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject = null)

驗證的返回類型是AuthorizeRequestValidationResult,它只是對ValidatedAuthorizeRequest的一個包裝。

所以對它的理解就兩點:返回類型是驗證過的授權請求ValidatedAuthorizeRequest,驗證過程是怎么樣的

它的主要驗證內容如下:

  1. 根據請求參數中的client_id去ids的配置中找到Client實體並賦值給ValidatedAuthorizeRequest,賦值時與client實體相關的配置信息也會設置到返回值上。值得注意的是這里並沒有去驗證客戶端密鑰
  2. 加載和驗證request object,參考:RequestObjectValues,這個可以忽略
  3. 驗證RedirectUri
  4. 驗證state, response_type, response_mode,主要就是看是否存在,是否是ids配置支持的,以及根據授權模式驗證某些參數是否符合邏輯
  5. 驗證scope,這里結合ids的配置對scope、客戶端、資源進行驗證
  6. 驗證輔助參數:nonce, prompt, acr_values, login_hint etc
  7. 最后是執行我們自定義的驗證器
  8. 最后驗證好的結果會設置到ValidatedAuthorizeRequest,然后返回

這里沒有詳細分析每一步驗證,但是我們可以有個大概印象,將來需要擴展或哪里不懂時只是設計到授權驗證時能想到去看這的源碼

授權參數存取器IAuthorizationParametersMessageStore

在ids執行授權過程中涉及到很多參數,比如cliaentid、securet、scope等,這些參數在需要在授權的多個步驟中傳遞,ids為此提供了一個存取器。

具體來說當mvc客戶端因為沒有登陸跳轉到ids4的登陸頁面時會攜帶一堆參數,當用戶輸入完賬號密碼登陸成功后會使用這個接口存儲跟授權相關的參數,然后回調ids4的.../auth../callback去做后續生成code的工作,在這個回調中會從querystring中拿到之前存儲授權參數生成的唯一key,通過這個key調用IAuthorizationParametersMessageStore拿到之前的完整的授權相關參數

默認實現有兩個:QueryStringAuthorizationParametersMessageStore(直接存儲在地址欄參數QueryString里)、DistributedCacheAuthorizationParametersMessageStore(使用分布式緩存接口IDistributedCache進行存取)。

ids中默認沒有在ioc容器中注冊任何授權參數的存取器

ReturnUrl轉換器IReturnUrlParser

當ids授權檢查時並不是直接從queryString中獲取客戶端提交的參數,而時從QueryString中的ReturnUrl參數中獲取客戶端id、scope等參數的。在集成ids網頁登陸過程中,當發起"/authorize"或“/callback”請求時,從ReturnUrl參數中獲取授權請求的參數,然后將其轉換為表示授權請求對象的AuthorizationRequest。先看看方法簽名

Task<AuthorizationRequest> ParseAsync(string returnUrl)

參數returnUrl就是queryString中的同名參數,這個參數里面包含授權請求的核心參數(如:clientid、scope、redirect_uri等),是客戶端通過ids的客戶端庫自動構建的。AuthorizationRequest可以等效於ValidatedAuthorizeRequest

內部邏輯如下:

  1. 判斷是否是請求的“/authorize”或“/callback”,若是則繼續,否則返回null
  2. 將參數轉換為NameValueCollection的形式
  3. 若是callback請求,嘗試通過參數存取器AuthorizationParametersMessageStore獲取前一步步驟存儲下來的授權參數
  4. 嘗試獲取當前用戶,若是“/authorize”就是匿名用戶,若是“/callback”就是前一步驟(“/authorize”)身份驗證正規的用戶,默認是基於cookie的
  5. 最后調用授權請求驗證器AuthorizeRequestValidator進行驗證,得到ValidatedAuthorizeRequest,並將其轉換為AuthorizationRequest進行返回

ReturnUrl轉換器容器ReturnUrlParser

它是IReturnUrlParser的容器,它的設計思路類似mvc中路由和路由容器的。當需要將returnUrl參數轉換為表示當前授權的請求AuthorizationRequest時,它遍歷內部的ReturnUrlParser,然后調用它進行轉換,只有有一個轉換成功,則跳出循環並返回結果

授權交互響應構建器IAuthorizeInteractionResponseGenerator

它用來確保請求用戶必須是經過身份驗證的,或授權確認哪些scope了的。定義如下:

Task<InteractionResponse> ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null)

request:驗證過的授權請求參數,里面包含當前用戶

consent:用戶的授權確認消息

此接口在AuthorizeEndpointBase.ProcessAuthorizeRequestAsync被用到,所以ids中所有端點(終結點)都會來做這步操作。默認實現是AuthorizeInteractionResponseGenerator。核心邏輯如下:

檢查用戶是否需要登陸:

  1. 若請求參數中存在PromptModes包含“Login”或“SelectAccount”,則直接返回說用戶需要登陸。此時會刪除這個參數,這樣下次執行到這里時就不會再提示了。
  2. 在用戶是已登陸時,通過IProfileService來設置用戶的激活狀態,所以這里給我留了個擴展點。默認是直接設置未已激活
  3. 若是匿名用戶或用戶未激活,則直接返回說用戶需要登陸。
  4. 授權請求參數中可能存在一個客戶端指定的idp(IdentityProvider),而用戶信息的IdentityProvider表示當前用戶當初是使用哪個身份驗證方案做的驗證,這里會做個對比,若客戶端發起授權請求的參數希望使用的身份驗證方案 與  當前用戶當初做身份驗證的那個方案不匹配,則直接返回說用戶需要登陸。
  5. 請求中可能存在一個MaxAge參數,它表示用戶的過期時長,單位秒。若授權請求的參數中有這個值,則會與當前用戶的GetAuthenticationTime()對比,若過期了,則直接返回說用戶需要登陸。
  6. 若用戶當初身份驗證用的那個方案就是使用的ids的本地身份驗證方案,但此時發現當前客戶端沒有開啟本地身份驗證方案,則直接返回說用戶需要登陸。
  7. 若客戶端配置了IdentityProviderRestrictions,則表示此客戶端只允許這幾種身份驗證方案,所以看看用戶當初使用的身份驗證方案在其中沒有,若沒有,則直接返回說用戶需要登陸。
  8. 若客戶端配置了UserSsoLifetime,它表示客戶端配置的用戶最大有效時長,單位秒,當前用戶登陸是否過期了,若過期了,則直接返回說用戶需要登陸。這跟步驟5不同,那里是當前請求參數的MaxAge,而這里是客戶端配置中的用戶有效時長

若上面檢查用戶需要登陸,就直接返回了,否則繼續根據用戶授權確認消息繼續判斷用戶是否需要重新登陸,邏輯如下:

檢查用戶授權確認信息:

首先確認是否必須要求用戶來確認授權范圍,具體怎么確認的后面再補充,只要知道在特定條件下是必須要用戶來確認授權范圍的

然后是看授權請求的參數中是否包含PromptModes提示模式,且里面包含Consent,此時也表示必須要用戶來確認授權范圍

當以上任意條件滿足時才檢查用戶確認授權的信息,否則不檢查,直接認為符合要求,進行返回,下面看看若滿足任意條件時如何檢查的

  1. 若用戶授權確認消息為空,則直接返回說需要用戶授權確認
  2. 若用戶在之前說拒絕授權,返回錯誤信息
  3. 若用戶確認過授權范圍,這拿到用戶允許的scope的列表
  4. 特定情況下,有些scope是必須允許的,此時看看用戶是否都允許了這些scope,若沒有的話,返回錯誤信息
  5. 此時說明一切ok,結合ids配置里的Resource、scope、客戶端請求的scope列表、用戶允許的scope列表 最終形成一個本次授權允許的資源及scope,這個結果會設置到當前授權請求的ValidatedResources屬性上
  6. 若客戶端配置了AllowRememberConsent,說明此客戶端允許保存此用戶的授權范圍保存下來。還要檢查用戶點擊確認授權是否勾選了記住授權,若都滿足則通過IConsentService將用戶對此客戶端的授權確認信息存儲下來,下次授權可以直接用了。

交互服務IIdentityServerInteractionService

集成ids提供的網頁登陸、第三方登陸、授權確認頁面等涉及到用戶與ids進行交互,這個交互過程用到的一些功能就封裝在IIdentityServerInteractionService接口中,主要用在ids處理網頁登陸的AccountCtroller中。默認實現:DefaultIdentityServerInteractionService,下面逐一描述每個方法

Task<AuthorizationRequest> GetAuthorizationContextAsync(string returnUrl)

在授權請求時參數中有個returnUrl,它里面攜帶了本次請求的重要參數(client_id、scope、等),此方法就是將returnUrl參數轉換為表示當前授權請求的AuthorizationRequest。在進入ids網頁登陸時、在網頁登陸中的第三方登陸回調時、在用戶登陸授權確認時都會使用到此方法

它啥都不干,將任務直接委托給ReturnUrlParser

DenyAuthorizationAsync

用戶拒絕授權,核心邏輯就是使用授權消息存儲器IConsentMessageStore將一條用戶拒絕授權的消息紀錄下來,默認時以加密cookie的形式存儲的。

集成網頁登陸后,處理了DenyAuthorizationAsync會繼續執行ids的 callback終結點 “idsServer/connect/authenzation/callback”

 

目前只研究到登陸部分,注銷、授權確認等操作會涉及到此接口的其它方法,將在后續補充說明

IdentityServer的終結點Endpoint

比如發起AuthenrozationCode流程時要向https://ids-server-domain/authenrozation發起請求,以獲取臨時code,后續的步驟還會訪問https://ids-server-domain/token通過臨時code換取token,在ids各種授權模式中還有好幾個這樣的地址,它們被定義為終結點(其實wcf、asp.net core現在的路由里 向這種一個被請求的地址都叫終結點,所以提到終結點你應該想到就是一個被請求的地址)

在ids中Endpoint = url + Handler(處理器類型),將來請求抵達時url用來匹配判斷當前請求是否與這個終結點匹配;Handler則被用來處理本次請求

終結點在ids服務器啟動階段配置(畢竟ids就那幾個固定的終結點嘛)。在請求抵達時匹配找到當前請求對應的終結點,根據終結點的Handler類型從容器中取出對應的IEndpointHandler,請求就由它處理,這些EndpointHandler當然是在AddIdentityServer中注冊的

IdentityServer的終結點EndpointRouter

請求抵達時,根據當前請求地址找到Endpoint的任務就是EndpointRouter來完成的,不過它多做了一步,找到當前Endpoint后直接根據Handler類型從容器中取出對應的IEndpointHandler

EndpointRouter也是在AddIdentityServer注冊的

攔截終結點請求的中間件IdentityServerMiddleware

比如此時有個客戶端使用授權碼模式向ids服務器的https://ids-server-domain/authenrozation發起請求,在asp.net core中肯定是有個中間件來攔截這個請求,這個中間件調用上面的EndpointRouter,如果找到一個匹配的IEndpointHandler,就讓它直接處理本次請求,否則啥都不干執行下一個中間件。IdentityServerMiddleware就是這個攔截ids相關請求的中間件

這個中間件是在UseIdentityServer中注冊的

它指向完EndpointHandler后返回一個Result,這個Result在中間件中被執行,以TokenResult為例,就是將token輸出json響應

授權響應生成器IAuthorizeResponseGenerator

無論是多種授權模式中的請求的響應,還是單種授權模式中的多個步驟的響應,幾乎都是通過此接口來完成的,比如授權碼模式響應code時、隱式模式Implicit中返回accessToken/idToeken等。所以它的作用是根據  當前已驗證過的授權請求對象ValidatedAuthorizeRequest,創建一個表示響應的AuthorizeResponse對象,此對象中可能包含code、accessToken、等,ids中統一用這個類型表示所有的授權響應,而不是為每種授權情形單獨定義。

先看看AuthorizeResponse對象的定義

 1 public class AuthorizeResponse
 2 {
 3     public ValidatedAuthorizeRequest Request { get; set; }
 4     public string RedirectUri => Request?.RedirectUri;
 5     public string State => Request?.State;
 6     public string Scope => Request?.ValidatedResources?.RawScopeValues.ToSpaceSeparatedString();
 7 
 8     public string IdentityToken { get; set; }
 9     public string AccessToken { get; set; }
10     public int AccessTokenLifetime { get; set; }
11     public string Code { get; set; }
12     public string SessionState { get; set; }13 }

創建授權碼響應核心邏輯如下:

  1. 創建一個表示授權碼的實體對象AuthorizationCode
    CreationTime    //創建時間
    ClientId         //客戶端id
    Lifetime         //有效期,單位秒
    Subject          //當前登陸的用戶
    SessionId        //一次授權應該是產生一個唯一的sessionid
    Description      //描述
    CodeChallenge    //是個啥簽名
    CodeChallengeMethod   //上面簽名的簽名算法,目前還不曉得在哪用,不重要,應該就是驗證此Code是否被串改過
    IsOpenId              //若授權的scope里包含 openId這個scope 則為true
    RequestedScopes       //本次授權請求最終能訪問的scope集合
    RedirectUri          //所有授權流程結束后用戶將跳轉到這個地址
    Nonce                  //隨機字符串,客戶端發起授權請求提供的那個隨機字符串餐宿
    StateHash              //客戶端發起授權請求時提供的狀態參數的hash值,因為這個參數是原樣返回給客戶端的,所以價格hash簽名,確保此狀態數據沒有變動過
    WasConsentShown        //是否要求用戶確認授權    

    這些數據都是直接獲取間接從表示當前授權請求的對象中來的,而這個對象是經過驗證過的,最終可以作為響應的數據源的

  2. 通過授權碼存取器IAuthorizationCodeStore將AuthorizationCode存儲到服務端,並參數一個字符串的唯一id
  3. 組織授權響應,返回
    var response = new AuthorizeResponse
    {
        Request = request,
        Code = id,
        SessionState = request.GenerateSessionStateValue()
    };

暫時就不說其它流程了,以后補充吧。不過值得注意的是里面並沒有單獨針對accessToken創建的流程。而隱式模式中包含accessToken和idToken的創建流程,因此猜測請求token時可能會映射到隱式Implicit模式的流程上去處理。

授權結果AuthorizeResult

這個設計類似mvc里IActionResult和各種Result的設計,ids中有個IEndpointResult接口,它表示ids中的終結點處理后的結果,ids中各種處理在響應前都會生成一個實現此接口的XXResult對象,在ids中間件中會來執行此結果,最終完成對請求的響應。

AuthorizeResult實現了IEndpointResult接口,它表示執行授權操作的結果,典型的就是ids的callbackEndpoint的處理結果,此終結點主要用來生成code。此終結點的最后會生成一個表示授權響應的AuthorizeResponse,它最終被轉換為AuthorizeResult。默認情況下主要的的邏輯是攜帶code,跳轉到客戶端的回調地址“xxxmvc/oidc-callback”,核心步驟如下:

  1. 通過ids提供的IUserSession將當前客戶端id存儲下來,便於后續做注銷處理。本質上是將客戶端id存儲到加密cookie的用戶票證里的。
  2. 由於默認回調客戶端是使用post方式,所以這里會動態創建一個form表單進行響應,內部有js,響應時直接提交,實現post跳轉到客戶端的回調地址。

終結點抽象類AuthorizeEndpointBase

所有終結點都集成至它。方法簽名如下:

Task<IEndpointResult> ProcessAuthorizeRequestAsync(NameValueCollection parameters, ClaimsPrincipal user, ConsentResponse consent)

它主要做了3件事

  1. 使用授權請求驗證器IAuthorizeRequestValidator對參數進行驗證,得到一個驗證通過的授權請求對象AuthorizeRequestValidationResult
  2. 驗證用戶對授權的確認信息(如果有的畫)
  3. 根據授權請求、授權模式創建一個存儲需要響應的數據對象並返回(參考

授權回調端點AuthorizeCallbackEndpoint

在集成ids網頁登陸時,用來發放code

  1. 准備請求參數
  2. 從前一步驟的身份驗證中獲取當前登陸用戶
  3. 調用父類的ProcessAuthorizeRequestAsync執行code的發放,會攜帶code跳轉到客戶端指定的回調頁面(/oidc-callback)

基於Cookie的消息存取器MessageCookie<TModel>

在ids4運行過程中可能需要在用戶cookie里存取一些數據,此對象就是來做這個工作的。比如使用ids4的登陸時,如啟用授權確認,則相關消息將間接使用這個對象來存取消息。

泛型TModel表示這個消息類型,寫入時:以 消息類型Id.消息Id的格式作為cookie名,將TModel序列號為json,然后進行加密作為cookie值,讀取時:反過來。

IdentityServerOptions.UserInteraction.CookieMessageThreshold控制消息的數量,若寫入cookie消息的數量大於這個值 則會刪除早期的cookie消息。

登陸授權確認時的消息的存取器ConsentMessageStore

在使用ids4提供的登陸功能時可以開啟登陸時由用戶確認授權,此時主要可以讓用戶選擇授權哪些scope,用戶做的這個選擇需要紀錄下來,以便ids的后續步驟使用,針對用戶授權確認的消息的存取、刪除功能就由ConsentMessageStore來完成。它內部使用上面說的MessageCookie<TModel>來實現的。被存取的消息里最重要的就是用戶允許的scope的列表

Token

一個實體類,定義在IdentityServer4.Model中的

  • AllowedSigningAlgorithms:允許的簽名算法列表
  • Audiences此token:一個字符串列表,指名這個token可以給誰用。對應到與在webApi配置jwtToken身份驗證時配置的那個字符串(並開啟aud驗證),這樣webApi服務器在驗證token時看自己並不在這個列表中,則認為驗證token也會失敗(當然token驗證還有其它幾個步驟)
  • Issuer:此token是誰發放的,一般就是ids4
  • CreationTime:創建時間
  • Type:一個字符串,一般是IdentityToken、AccessToken,可能還有其它類型的吧
  • ClientId:客戶端id
  • AccessTokenType:如果是AccessToken,則可能是jwt或Reference
  • Claims:token內部包含的cliaim,webApi驗證token后可以從中取得這些cliam,可以在配置ids4的scope時指定生成token時里面方哪些claim,比如用戶的“所屬部門”,這樣webApi服務器這邊可以拿到當前登陸用戶的claim,結合asp.net core的基於策略的授權可以做靈活的權限控制
  • SubjectId:用戶唯一Id,從Claims去的SubectId
  • SessionId:ids4做身份驗證后會生成一個sessionId,唯一id,多次身份驗證生成sessionId不一樣,可以理解為它表示一次身份驗證
  • Scopes:客戶端允許的scope、客戶端請求的、用戶授權確認時允許的 三者的交集,最終表示此token能訪問的scope

解析后密鑰ParsedSecret

簡單點理解它就是 Id + 密碼 + 機密類型 + 附加數據。比如:客戶端client_id + 客戶端密鑰client_securet + ParsedSecretTypes.SharedSecret

此對象目前我曉得的在客戶端哪code時 需要攜帶客戶端密鑰,此時會用到這個對象,ids會從客戶端請求過來的客戶端id,客戶端密鑰來創建此對象。具體怎么用的后續文章會分析。

先看看它的定義

1 public class ParsedSecret
2 {
3     public string Id { get; set; }          //此機密對象的唯一id
4     public object Credential { get; set; }  //憑據對象,簡單的情況下可能是個字符串形式的密碼,復雜的可能是個證書
5     public string Type { get; set; }        //機密類型,任意字符串,但模式是常量中定義的
6     public Dictionary<string, string> Properties { get; set; } = new Dictionary<string, string>(); //可以用它存點附加數據
7 }

這里的機密類型是對機密的一種分類,不好描述,它以常量的形式定義,感受下吧

1 public static class ParsedSecretTypes
2 {
3         public const string NoSecret = "NoSecret";
4         public const string SharedSecret = "SharedSecret";
5         public const string X509Certificate = "X509Certificate";
6         public const string JwtBearer = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
7 }

密鑰解析器ISecretParser

它的任務是從當前Http請求解析得到解析后的機密ParsedSecret

比如在客戶端攜帶code 客戶端密鑰 之類的 找ids請求token時,默認是通過post一個表達向ids發起請求的,就有個PostBodySecretParser的實現類,它從請求的表單中拿到客戶端id和密鑰,然后創建一個解析后的機密對象ParsedSecret,為后續驗證客戶端做准備。

密鑰解析器容器ISecretsListParser

又是這種經典的設計,它是ISecretParser的容器,它遍歷內部的機密解析器ISecretParser,只要有一個找到了,則跳出遍歷。

密鑰驗證器ISecretValidator

它根據ids中配置的密鑰列表和當前請求提供的密鑰做驗證,接口定義如下:

Task<SecretValidationResult> ValidateAsync(IEnumerable<Secret> secrets, ParsedSecret parsedSecret);

secrets:從ids配置(可能在數據庫)中拿到密鑰配置。如:某個客戶端配置的密鑰列表

parsedSecret:當前請求中得到的密鑰信息

SecretValidationResult:返回值,里面包含一個Confirmation的字符串,不曉得是啥,反正是驗證后得到的一個字符串,會存儲到名為cnf的claim中

此接口有好幾個實現類,這里是理解為主,因此我們分析下其中的HashedSharedSecretValidator,核心邏輯如下:

  1. 過濾secrets參數,只獲取類型為IdentityServerConstants.SecretTypes.SharedSecret的密鑰
  2. 對parsedSecuret做hash256和512計算
  3. 遍歷過濾后的過濾secrets,與parsedSecuret的hash值做比對,一旦匹配到成功則跳出循環,直接返回結果

此實現類沒有設置返回值的Confirmation屬性

猜想密鑰材料主要是用來簽名,而這里的密鑰驗證是對client密鑰進行驗證,apiResource好像也可以單獨設置密鑰

密鑰驗證器容器ISecretsListValidator

又是這種經典設計,它是密鑰驗證器的容器,當驗證密鑰時遍歷內部的驗證器進行驗證,只要有一個成功則跳出遍歷后返回

客戶端密鑰驗證器IClientSecretValidator

接口名很貼切,它使用密鑰驗證器ISecretValidator對客戶端進行密鑰驗證,如:攜帶客戶端id 密碼 code 向ids請求token時,ids會通過它來驗證客戶密鑰。核心流程如下:

  1. 使用密鑰解析器ISecretsListParser從當前請求獲取密碼
  2. 通過客戶端存取器(可能是數據庫或硬編碼配置)獲取客戶端實體
  3. 若客戶端配置RequireClientSecret不是必須的,或者client.IsImplicitOnly()則認為直接驗證通過
  4. 否則使用密鑰驗證器ISecretsListValidator進行驗證,注意:若驗證器有返回Confirmation則設置到返回結果的同名屬性上

token請求驗證器ITokenRequestValidator

客戶端攜帶客戶端id 密碼 和其它參數向ids請求token時會使用此接口驗證此請求,核心邏輯如下:

  1. 將客戶端、密鑰、和密鑰驗證結果中的Confirmation設置到當前token請求對象ValidatedTokenRequest上
  2. 檢查客戶端的協議類型是否是 IdentityServerConstants.ProtocolTypes.OpenIdConnect
  3. 檢查請求參數中是否包含了授權類型字段
  4. 檢查授權類型的字符串長度是否超過ids選項對象的_options.InputLengthRestrictions.GrantType
  5. 將授權類型設置到當前token請求對象ValidatedTokenRequest上
  6. 最后根據授權類型做不同的驗證
     1 switch (grantType)
     2             {
     3                 case OidcConstants.GrantTypes.AuthorizationCode:
     4                     return await RunValidationAsync(ValidateAuthorizationCodeRequestAsync, parameters);
     5                 case OidcConstants.GrantTypes.ClientCredentials:
     6                     return await RunValidationAsync(ValidateClientCredentialsRequestAsync, parameters);
     7                 case OidcConstants.GrantTypes.Password:
     8                     return await RunValidationAsync(ValidateResourceOwnerCredentialRequestAsync, parameters);
     9                 case OidcConstants.GrantTypes.RefreshToken:
    10                     return await RunValidationAsync(ValidateRefreshTokenRequestAsync, parameters);
    11                 case OidcConstants.GrantTypes.DeviceCode:
    12                     return await RunValidationAsync(ValidateDeviceCodeRequestAsync, parameters);
    13                 default:
    14                     return await RunValidationAsync(ValidateExtensionGrantRequestAsync, parameters);
    15             }

     

token端點ToekenEndpoint

  1. 使用客戶端密鑰驗證器IClientSecretValidator驗證客戶端
  2. 使用請求驗證器驗證當前token請求
  3. 創建token響應並返回

 

未完待續...

目前只在研究登陸部分,涉及到的核心類在其它流程中也可能被使用到,后續研究其它流程中涉及到的核心類的說明也會在這里補充...

 


免責聲明!

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



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