1. 程式人生 > >IdentityServer4 實現 OpenID Connect 和 OAuth 2.0

IdentityServer4 實現 OpenID Connect 和 OAuth 2.0

http key 嚴格 簡單 alt 三種 pan 清除 推送

原文:IdentityServer4 實現 OpenID Connect 和 OAuth 2.0

OAuth 2.0 。順便說一下個人理解授權與認證的區別,授權可以理解為房間的主人允許你進入他的房間,但是房間的主人不知道你是誰。認證就是讓房間的主任知道你是誰。

OAuth 2.0 簡介

OAuth 2.0是OAuth的升級版本,是個授權協議,總的來說就是允許應用提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據,。它可以讓那些控制資源的人允許某個應用以代表他們來訪問他們控制的資源, 這個應用從資源的所有者那裏獲得到授權(Authorization)和access token, 隨後就可以使用這個access token來訪問資源。

OpenID Connect 簡介

OpenID Connect是建立在OAuth2協議上的一個簡單的身份標識層, 是一個基於OAuth2協議的身份認證標準協議。所以OpenID Connect兼容OAuth2.。使用OpenID Connect, 客戶端應用可以請求一個叫identity token的Id token, 它會和access token一同返回給客戶端應用. 這個Id token就可以被用來登錄客戶端應用程序, 而這個客戶端應用還可以使用access token來訪問API資源。OpenID Connect還定義了一個UserInfo端點, (OAuth2定義了Authorization端點和Token端點)它允許客戶端應用獲取用戶的額外信息.。

IdentityServer4 簡介

Identityserver是一個實現了OpenID Connect和OAuth 2.0框架。它實現了這兩種協議流程,也提供了客戶端以便於集成。

OAuth 2.0中的4個成員

在OAuth2.0中有4個成員,Resource Owner、Client、Resource Server、Authorization Server,如圖所示:

技術分享圖片

授權服務器和資源服務器可以在同一個項目中。

OAuth 2.0授權流程

技術分享圖片

流程解析:

  1. 用戶打開客戶端,客戶端要求向資源所有者(即用戶)給予授權;
  2. 用戶同意授權;
  3. 客戶端得知用戶同意授權後,向授權服務器獲取授權;
  4. 授權服務器給予客戶端授權,並將授權碼(Access Token);
  5. 客戶端攜帶授權碼去請求資源服務器;
  6. 資源服務器將受限的資源開放給客戶端。

OAuth 2.0授權模式

授權模式是表示客戶用來獲取訪問令牌的資源所有者授權的憑證。此規範協議規定了4種授權類型:

  • client credentials(客戶端模式)
  • resource owner password credentials(密碼模式)
  • authorization code(授權碼模式)
  • implicit(簡化模式)

下面詳細說明各種授權模式的具體流程:

client credentials(客戶端模式)

客戶端模式是4種模式中最簡單的一種模式。客戶端可以使用客戶端憑據請求訪問令牌(或者其他支持的認證方式),在這種模式中,客戶端占據主導地位,它不需要用戶的同意,可以直接向授權服務器索取令牌,嚴格來說,該模式並不存在授權的問題,流程如下:

技術分享圖片

  1. 客戶端向授權服務器發起請求索要令牌;
  2. 授權服務器將令牌發放給客戶端。
  3. 客戶端用令牌請求資源服務器

(A步驟)中所需的參數::

  • grant_type 必選項 此值必須為client_crendentials
  • scope 可選項

例如:

POST /token HTTP/1.1

Host: server.example.com

Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials

(B步驟)授權服務器返回結果:

HTTP/1.1 200 OK

Content-Type: application/json;charset=UTF-8

Cache-Control: no-store

Pragma: no-cache

{

"access_token":"2YotnFZFEjr1zCsicMWpAA",

"token_type":"example",

"expires_in":3600,

"example_parameter":"example_value"

}

resource owner password credentials(密碼模式)

密碼模式適合建立在客戶端與資源所有者具有信任關系的情況下,例如它是一個設備的操作系統或者具有很高權限的應用。這種模式用戶要向客戶端提供自己的用戶名密碼,從而達到向服務提供商索取授權。授權服務器在啟動此類型時,要特別小心,只有在其他的授權方式不被允許的情況下才可以使用這種授權模式。流程如下:

技術分享圖片

  1. 客戶端要求使用資源所有者的密碼;
  2. 資源所有者給予用戶名密碼後,客戶端向授權服務器發起申請令牌的請求;
  3. 授權服務器將令牌發放給客戶端。
  4. 客戶端用令牌請求資源服務器。

(B步驟)所需參數:

  • grant_type 必選項 此值必須為"password"
  • username 必選項 用戶名
  • password 必選項 密碼
  • scope 可選項

例如:

POST /token HTTP/1.1

Host: server.example.com

Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW

Content-Type: application/x-www-form-urlencoded

grant_type=password&username=johndoe&password=A3ddj3w

(C步驟)發放令牌:

HTTP/1.1 200 OK
     Content-Type: application/json;charset=UTF-8
     Cache-Control: no-store
     Pragma: no-cache
     {
       "access_token":"2YotnFZFEjr1zCsicMWpAA",
       "token_type":"example",
       "expires_in":3600,
       "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
       "example_parameter":"example_value"
     }

Authorization code(授權碼模式)

授權代碼授權類型用於獲取訪問令牌和刷新令牌,並針對機密客戶端進行優化。它是一個基於重定向的流程,因此客戶端必須能夠與資源所有者的用戶代理(通常是Web瀏覽器)並且能夠從授權服務器接收傳入請求(通過重定向)。授權碼模式是功能最完整、流程最嚴密的授權模式,它的特點就是通過客戶端的後臺服務器,與"服務提供商"的授權服務器進行互動。授權流程如下:

技術分享圖片

流程解析:

  1. 客戶端通過用戶代理,重定向請求授權服務器,所需參數:客戶端標識及重定向客戶端URL;
  2. 用戶選擇是否給予客戶端授權;
  3. 授權服務器給予客戶端一個認證授權碼,並跳轉到指定的客戶端URI(為不重1中的URL);
  4. 客戶端使用該授權碼,重定向到授權服務器,獲取令牌;
  5. 授權服務器校驗該授權碼以及重定向的URI後,向客戶端發送令牌或者更新令牌。(此時會告知客戶端清除授權碼,如果授權碼是存放在cookie中,會清除授權碼的cookie)
  6. 客戶端用令牌請求資源服務器

步驟(A)所需的幾個參數:

  • response_type 必選項 表示的是要求指定的授權類型,此處必須設置為:code
  • client_id 必選項 客戶端的唯一標識
  • redirect_uri 可選項 重定向的URI
  • scope 可選項 授權的管道
  • state 建議項 表示客戶端狀態,授權服務器會將該狀態原值返回

問題答疑:為何引入authorization_code?

協議設計中,為什麽要使用authorization_code來交換access_token?這是讀者容易想到的一個問題。也就是說,在協議的第3步,為什麽不直接將access_token通過重定向方式返回給Client呢?比如:

HTTP/1.1 302
Location:
https://www.facebook.com/?access_token=ya29.AHES6ZSXVKYTW2VAGZtnMjD&token_type=Bearer&expires_in=3600

如果直接返回access_token,協議將變得更加簡潔,而且少一次Client與AS之間的交互,性能也更優。那為何不這麽設計呢?協議文檔[1]中並沒有給出這樣設計的理由,但也不難分析:

(1) 瀏覽器的redirect_uri是一個不安全信道,此方式不適合於傳遞敏感數據(如access_token)。因為uri可能通過HTTP referrer被傳遞給其它惡意站點,也可能存在於瀏覽器cacher或log文件中,這就給攻擊者盜取access_token帶來了很多機會。另外,此協議也不應該假設RO用戶代理的行為是可信賴的,因為RO的瀏覽器可能早已被攻擊者植入了跨站腳本用來監聽access_token。因此,access_token通過RO的用戶代理傳遞給Client,會顯著擴大access_token被泄露的風險。 但authorization_code可以通過redirect_uri方式來傳遞,是因為authorization_code並不像access_token一樣敏感。即使authorization_code被泄露,攻擊者也無法直接拿到access_token,因為拿authorization_code去交換access_token是需要驗證Client的真實身份。也就是說,除了Client之外,其他人拿authorization_code是沒有用的。 此外,access_token應該只頒發給Client使用,其他任何主體(包括RO)都不應該獲取access_token。協議的設計應能保證Client是唯一有能力獲取access_token的主體。引入authorization_code之後,便可以保證Client是access_token的唯一持有人。當然,Client也是唯一的有義務需要保護access_token不被泄露。

(2) 引入authorization_code還會帶來如下的好處。由於協議需要驗證Client的身份,如果不引入authorization_code,這個Client的身份認證只能通過第1步的redirect_uri來傳遞。同樣由於redirect_uri是一個不安全信道,這就額外要求Client必須使用數字簽名技術來進行身份認證,而不能用簡單的密碼或口令認證方式。引入authorization_code之後,AS可以直接對Client進行身份認證(見步驟4和5),而且可以支持任意的Client認證方式(比如,簡單地直接將Client端密鑰發送給AS)。

在我們理解了上述安全性考慮之後,讀者也許會有豁然開朗的感覺,懂得了引入authorization_code的妙處。那麽,是不是一定要引入authorization_code才能解決這些安全問題呢?當然不是。筆者將會在另一篇博文給出一個直接返回access_token的擴展授權類型解決方案,它在滿足相同安全性的條件下,使協議更簡潔,交互次數更少。

implicit(簡化模式)

簡化模式用於獲取訪問令牌(但它不支持令牌的刷新,只所以稱為簡化模式,和授權碼模式比少了獲取授權碼的步驟),並對運行特定重定向URI的公共客戶端進行優化,而這一些列操作通常會使用腳本語言在瀏覽器中完成,令牌對訪問者是可見的,且客戶端也不需要驗證。具體流程如下:

技術分享圖片

註:Web-HostedClientResource服務器相當於是一個存儲accessToken的地方,通常指瀏覽器中的存儲(cookie、localStorage、SessionStorge、js變量等)

步驟解析:

  1. 客戶端攜帶客戶端標識以及重定向URI到授權服務器;
  2. 用戶確認是否要授權給客戶端;
  3. 授權服務器得到許可後,跳轉到指定的重定向地址,並將令牌也包含在了裏面;
  4. 客戶端不攜帶上次獲取到的包含令牌的片段,去請求資源服務器;
  5. 資源服務器會向瀏覽器返回一個腳本;
  6. 瀏覽器會根據上一步返回的腳本,去提取在C步驟中獲取到的令牌;
  7. 瀏覽器將令牌推送給客戶端。

(A步驟)中需要用到的參數,註意在這裏要使用"application/x-www-form-urlencoded"格式:

  • response_type 必選項,此值必須為"token"
  • client_id 必選項
  • redirect_uri 可選項
  • scope 可選項
  • state 建議選項

    例如:

GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
    Host: server.example.com
					

(C步驟)中返回的參數包含:

  • access_token 必選項
  • token_type 必選項
  • expires_in 建議選項
  • scope 可選項
  • state 必選項

    例如:

HTTP/1.1 302 Found
     Location: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA
               &state=xyz&token_type=example&expires_in=3600

OpenID Connect授權模式

認證流程主要是由OAuth2的幾種授權流程延伸而來的,有以下3種:

  • Authorization Code Flow(授權碼模式):基於OAuth2的授權碼來換取Id Token和Access Token。
  • Implicit Flow(簡化模式):基於OAuth2的Implicit流程獲取Id Token和Access Token。
  • Hybrid Flow:混合Authorization Code Flow+Implici Flow獲取Id Token和Access Token。

註:OpenID Connect 為什麽沒有基於OAuth2的Resource Owner Password Credentials Grant和Client Credentials Grant擴展,Resource Owner Password Credentials Grant是需要應用提供賬號密碼的,賬號密碼都有了在獲取Id Token意義不大。Client Credentials Grant沒有用戶的參與所以獲取Id Token 也沒意義。這也能反映授權和認證的差異,以及只使用OAuth2來做身份認證的事情是遠遠不夠的,也是不合適的。

Authorization Code Flow(授權碼模式)

此模式是基於OAuth2的Authorization Code模式。流程和OAuth2的Authorization Code基本一致,只是在獲取access Token的時候會返回Id Token。授權流程如下:

技術分享圖片

流程解析:

  1. 客戶端通過用戶代理,重定向請求授權服務器,所需參數:客戶端標識及重定向客戶端URL;
  2. 用戶選擇是否給予客戶端授權;
  3. 授權服務器給予客戶端一個認證授權碼,並跳轉到指定的客戶端URI(為不重1中的URL);
  4. 客戶端使用該授權碼,重定向到授權服務器,獲取令牌(access token)和Id token;
  5. 授權服務器校驗該授權碼以及重定向的URI後,向客戶端發送令牌(access token)或者更新令牌(refresh token)以及Id token。(此時會告知客戶端清除授權碼,如果授權碼是存放在cookie中,會清除授權碼的cookie)
  6. 客戶端用令牌請求資源服務器,通過Id token 解析出用戶信息

步驟(A)所需的幾個參數:

  • response_type 必選項 表示的是要求指定的授權類型,此處必須設置為:code
  • client_id 必選項 客戶端的唯一標識
  • redirect_uri 可選項 重定向的URI
  • scope 可選項 授權的管道
  • state 建議項 表示客戶端狀態,授權服務器會將該狀態原值返回

Implicit Flow(簡化模式)

簡化模式用於獲取訪問令牌(但它不支持令牌的刷新,只所以稱為簡化模式,和授權碼模式比少了獲取授權碼的步驟),並對運行特定重定向URI的公共客戶端進行優化,而這一些列操作通常會使用腳本語言在瀏覽器中完成,令牌對訪問者是可見的,且客戶端也不需要驗證。具體流程如下:

此模式是基於OAuth2的implicit模式。流程和OAuth2的implicit基本夷之,只是在獲取access Token的時候會返回Id Token。授權流程如下:

技術分享圖片

註:Web-HostedClientResource服務器相當於是一個存儲accessToken的地方,通常指瀏覽器中的存儲(cookie、localStorage、SessionStorge、js變量等)

步驟解析:

  1. 客戶端攜帶客戶端標識以及重定向URI到授權服務器;
  2. 用戶確認是否要授權給客戶端;
  3. 授權服務器得到許可後,跳轉到指定的重定向地址,並將令牌和Id token也包含在了裏面;
  4. 客戶端不攜帶上次獲取到的包含令牌的片段,去請求資源服務器;
  5. 資源服務器會向瀏覽器返回一個腳本;
  6. 瀏覽器會根據上一步返回的腳本,去提取在C步驟中獲取到的令牌;
  7. 瀏覽器將令牌和Id token推送給客戶端。

(A步驟)中需要用到的參數,註意在這裏要使用"application/x-www-form-urlencoded"格式:

  • response_type 必選項,此值為"token" 或者"Id_token"或者"token Id_token"
  • client_id 必選項
  • redirect_uri 可選項
  • scope 可選項(如果 reponse_type為"token",此時scope中不能包含身份認證的scope,為"Id_token"時反之)
  • state 建議選項

Hybrid Flow(混合模式)

Hybrid流程是前兩者的混合, 在該流程裏, 有一些tokens和授權碼來自於授權端點, 而另外一些tokens則來自於Token端點。該流程允許客戶端立即使用ID Token, 並且只需要一次往返即可獲得授權碼。這種流程也要求客戶端應用可以安全的維護secret。

Hybrid流程的步驟如下:

  1. 客戶端準備身份認證請求, 請求裏包含所需的參數
  2. 客戶端發送請求到授權服務器
  3. 授權服務器對最終用戶進行身份認證
  4. 授權服務器獲得最終用戶的同意/授權
  5. 授權服務器把最終用戶發送回客戶端, 同時帶著授權碼, 根據響應類型的不同, 也可能還帶著一個或者多個其它的參數.
  6. 客戶端使用授權碼向Token端點請求一個響應
  7. 客戶端接收到響應, 響應的body裏面包含著ID Token 和 Access Token
  8. 客戶端驗證ID Token, 並獲得用戶的一些身份信息.

而根據其response_type的不同, 它又分為三種情況:

  • response_type=code id_token
  • response_type=code token
  • response_type=code id_token token

response_type=code id_token:

當reponse_type為這種類型的時候, 授權碼和ID Token從授權端點發行返回, 然後Access Token 和 ID Token會從Token端點發行返回:

技術分享圖片

response_type=code token:

當reponse_type為這種類型的時候, 授權碼和Access Token從授權端點發行返回, 然後Access Token 和 ID Token會從Token端點發行返回:

技術分享圖片

response_type=code id_token token:

當reponse_type為這種類型的時候, 授權碼和Access Token和ID Token從授權端點發行返回, 然後Access Token 和 ID Token會從Token端點發行返回:

技術分享圖片

OAuth 2.0 vs OpenID Connect 主要授權方式/流程對比

技術分享圖片

流程特點的比較

技術分享圖片

Refresh Token獲取Access Token

  • grant_type 必選項,此值為"refresh_token"
  • refresh_token 必選項
  • client_id 必選項
  • client_secret 必選項
  • scope 可選項

技術分享圖片

IdentityServer實現源碼:下載源碼

參考:https://www.cnblogs.com/Allen0910/p/8647935.html

   https://www.cnblogs.com/Tony100/p/9456755.html

   https://www.cnblogs.com/stulzq/p/8119928.html

IdentityServer

IdentityServer4 實現 OpenID Connect 和 OAuth 2.0