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

7m魚發表於2021-02-01
  上一篇文章實現了IdentityServer4與Asp.net core Identity的整合,可以使用通過identity註冊功能新增的使用者,以Password的方式獲取Access token,但是無論是Client Credentials還是Password流程它都是OAuth2.0的流程,本篇文章就來先介紹一下關於OpenIDConnect的基本概念和用法。
本文有以下內容:

OpenIDConnect介紹及基本概念

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

   至於oidc如何實現的,詳情可以檢視文件:https://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身份驗證的身份驗證處理器:

  

  然後啟動專案,並訪問受保護的資源:https://localhost:51001/WeatherForecast
  就會跳轉到身份驗證伺服器的登入頁面:

   下面是登入頁面url資訊:

  可以看到三個引數:
  1、第一個引數是因為客戶端向Identity伺服器發起身份驗證請求(Authentication Request),但由於終端使用者還未登入,所以先跳轉登入頁面,當完成登陸後將返回/connect/authorize/callback。
  2、第三個引數是響應型別,值為code,代表當前使用授權碼流程
  3、第二個引數重定向uri是完成授權後,將攜帶授權碼重定向的地址,也就是web Api程式的oidc身份驗證地址。
  對於授權碼流程的八個步驟來說,它完成了前三個,目前處於終端使用者身份驗證(未完成-還未輸入使用者名稱密碼)階段:
  
  輸入使用者名稱密碼登入後,就可以看到受保護的內容了:
  
  這裡完成授權碼流程的後四個步驟:
   
  這四個步驟都是由客戶端的oidc身份驗證處理器完成的(具體實現參見:https://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的授權終結點:
  https://localhost:5001/connect/authorize?response_type=id_token token&scope=openid scope2&client_id=test1&state=22222&redirect_uri=https://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應用程式中是如何體現的呢?下篇文章就來聊聊這個問題。
 
 
參考:
 

相關文章