從零搭建一個IdentityServer——初識OpenIDConnect

  上一篇文章實現了IdentityServer4與Asp.net core Identity的集成,可以使用通過identity註冊功能添加的用戶,以Password的方式獲取Access token,但是無論是Client Credentials還是Password流程它都是OAuth2.0的流程,本篇文章就來先介紹一下關於OpenIDConnect的基本概念和用法。
本文有以下內容:

OpenIDConnect介紹及基本概念

  根據OpenIDConnect的定義簡單來說,OpenIDConnect=(Identity, Authentication)+Oauth2.0(來自://openid.net/connect/faq/
另外從下面的回答可以看出OAuth2.0是一個身份驗證/授權框架,而OpenID Connect基於這些提供了身份標識功能,身份標識就是「是誰」的 問題。

   至於oidc如何實現的,詳情可以查看文檔://openid.net/specs/openid-connect-core-1_0.html,從文檔中可以找到這樣幾個關鍵詞:ID Token、Authentication、Authentication Request、Authorization Code Flow、Implicit Flow、Hybrid Flow、Response_Type、Authorization Endpoint、Token Endpoint。

  以及兩個表,OIDC身份驗證流程表:
  
  OIDC身份驗證請求對應的相應類型(Response_Type)表:
  
  還有一個流程圖:
   
  從以上關鍵詞可以獲得下面的信息:
  • ID Token:是一個包含特定聲明(Claim)的jwt,特定的聲明指的是身份驗證服務器驗證終端用戶時候產生的供客戶端使用的信息,如發行人(issuer)、最終用戶標識(sub)、客戶端id(aud)、過期時間(exp)、Token的發佈時間(iat)、用戶身份驗證時間(auth_time)等,另外也可以包含其它的聲明。ID Token由身份驗證服務器(IdentityServer4,OP)頒發,交由客戶端(RP)進行驗證。
  • Authentication:由IdentityServer4(OP)提供的身份驗證(登錄),最終用戶通過IdentityServer4(OP)的身份驗證(登錄)後,就可以發起Authentication Request,或者說如果在發起Authentication Request時用戶未進行身份驗證時將重定向到身份驗證界面進行身份驗證。
  • Authentication Request:向IdentityServer4(OP)的授權終結點發起的,用於獲取ID Token、授權碼(Authorization Code)甚至是訪問Token(Access Token)的請求。
  • Authorization Code Flow、Implicit Flow、Hybrid Flow:Authentication Request的三種不同請求流程。
  • Response_Type:Authentication Request的參數之一,根據設定該參數來決定使用哪一種請求流程。
  • Authorization Endpoint:授權終結點,用於接收Authentication Request。
  • Token Endpoint:令牌終結點,用於接收訪問令牌(Access Token)獲取請求。
   
  從兩個表格可以知道:三種身份驗證流程的特性,如各Token從哪個終結點返回、是否向用戶代理(如瀏覽器)透漏Token、是否支持刷新Token等。三種身份驗證流程通過指定Response_Type來決定,如參數值為code時進行授權碼流程Id_token以及id_token token時進行隱式流程,包含code以及token時為混合流程
   
  流程圖可以了解到OpenIDConnect的整個身份驗證及授權步驟,而最終具體的實現就直接對應到授權碼流程、隱式流程和混合流程。

OIDC授權碼流程及實現

  下面以授權碼流程為例進行詳細解說,首先授權碼流程步驟如下:
  
  授權碼流程一共有八個步驟,簡單來說就是由客戶端向身份驗證服務器發起身份驗證請求(響應類型為code),身份驗證服務器向用戶進行身份驗證及用戶允許和授權操作後,將授權碼發送給客戶端,客戶端通過授權碼向身份驗證服務器的Token中階段獲取ID和Access Token,然後對ID Token進行驗證並獲取用戶的ID信息。
 
  接下來使用之前創建的IdentityServer來實現基於授權碼流程的身份驗證與授權。
  上一篇文章已經對IdentityServer添加了用戶及Asp.net Core Identity組件的支持,目前無需再進行任何修改,換句話說現在的IdentityServer已經能夠頒發ID Token了,完成授權碼流程僅需要Client的支持,Client我們使用之前文章中添加的Web API來演示,把原有的基於Jwt Bearer的身份驗證代碼注釋掉,添加基於cookie以及OIDC的身份驗證服務(註:添加OIDC的時候Client信息需要與IdentityServer中數據庫一致,並且相應的Client配置的重定向地址需要與該應用程序匹配):
  

   確保「interactive」這個客戶端的重定向地址為「應用程序地址/signin-oidc」,這裡需要注意的是這個重定向地址實際上是客戶端(WebApi)通過方法.AddOpenIdConnect添加的用於處理odic身份驗證的身份驗證處理器:

  

  然後啟動項目,並訪問受保護的資源://localhost:51001/WeatherForecast
  就會跳轉到身份驗證服務器的登錄頁面:

   下面是登錄頁面url信息:

  可以看到三個參數:
  1、第一個參數是因為客戶端向Identity服務器發起身份驗證請求(Authentication Request),但由於終端用戶還未登錄,所以先跳轉登錄頁面,當完成登陸後將返回/connect/authorize/callback。
  2、第三個參數是響應類型,值為code,代表當前使用授權碼流程
  3、第二個參數重定向uri是完成授權後,將攜帶授權碼重定向的地址,也就是web Api程序的oidc身份驗證地址。
  對於授權碼流程的八個步驟來說,它完成了前三個,目前處於終端用戶身份驗證(未完成-還未輸入用戶名密碼)階段:
  
  輸入用戶名密碼登錄後,就可以看到受保護的內容了:
  
  這裡完成授權碼流程的後四個步驟:
   
  這四個步驟都是由客戶端的oidc身份驗證處理器完成的(具體實現參見://github.com/dotnet/aspnetcore/blob/main/src/Security/Authentication/OpenIdConnect/src/OpenIdConnectHandler.cs)
  整個過程主要是應用程序(Client)和身份驗證服務器進行交互並完成,相關的授權碼、ID token以及Access Token均「未」發送到瀏覽器中,所以授權碼流程也是最為安全的流程。
  如何在客戶端獲得相應的Access Token呢?通過HttpContext.GetTokenAsync(“token name”);即可獲得相應的Token:
  ID Token:

  Access Token:

  可以通過HttpContext獲取Token的原因是在添加oidc身份驗證服務時,將SaveTokens選項設置為true,當身份驗證成功後程序會自動將各類Token存儲到AuthenticationProperties中,最終加密寫入Cookie裏面,所以雖然數據保存在瀏覽器,但由於加密緣故,所以之前才說他們均「未」發送到瀏覽器中:
  那麼授權碼流程最後一步要如何實現呢?這裡有兩種方法,其一是通過獲取access token之後直接在程序中訪問身份驗證服務器的UserInfo Endpoint獲取,另外就是將oidc選項的GetClaimsFromUserInfoEndpoint設置為true即可:
  
  未獲取UserInfo的User Claims信息:
  
  獲取UserInfo後的User Claims信息,可以看到多了一個name的claim(關於為什麼只有一個claim後續文章中再進行說明):
  

OIDC隱式流程及實現

  既然最複雜的授權碼流程已經能夠實現了,那麼簡單的隱式流程肯定沒問題,下面就演示一下如何通過隱式流程將token直接獲取到瀏覽器中。
  首先創建一個支持隱式流程的client:
  
  註:數據庫中創建client複製已有的數據修改即可,client密碼是加密存儲的,複製後使用被複制的client密碼即可,另外因為需要將token信息發送到瀏覽器,所以需要將client信息中的「AllowAccessTokenViaBrowser」設置為1。
  將client的信息配置到client應用上:
  
  隱式流程要求響應類型為id_token或id_token及token。
  配置完成後運行程序並攜帶以下參數,直接訪問IdentityServer的授權終結點:
  //localhost:5001/connect/authorize?response_type=id_token token&scope=openid scope2&client_id=test1&state=22222&redirect_uri=//localhost:51001/swagger/index.html&nonce=11111
  登錄成功後程序將自動跳轉到參數redirect_uri指定的路徑,並且攜帶token及相關信息:
  將整個url格式化後獲得以下結果:

小結

  本篇文章介紹了OpenIDConnect的基本概念,並通過已有的IdentitySever程序演示了基於授權碼和隱式流程,其中授權碼模式是一種較為安全的模式,所有的關鍵的數據包括授權碼以及各類token均在client的後台完成,如果需要可以把相關的token加密後以cookie的方式放到客戶端以供後續使用。而隱式模式可以將各類token返回到瀏覽器中,這種模式可以在單頁應用中使用,將token交由js來進行管理並用於受保護資源的訪問。
  另外到目前為止我們可以看到的是IdentityServer或者說IdentityServer4的作用就是校驗Client信息、終端用戶的用戶名密碼信息,然後生成授權碼以及各類token,而token的驗證和使用實際上還是在Client中進行的。
  最後OIDC是一個身份驗證協議,那麼身份驗證和授權在Asp.net core應用程序中是如何體現的呢?下篇文章就來聊聊這個問題。
 
 
參考: