資源授權?對OAuth2.0的一次重新認識的過程

跳躍的鍵盤手發表於2021-02-26

什麼是OAuth?

OAuth一個開放的授權標準允許使用者在不提供關鍵資訊(如賬號,密碼)給第三方應用的前提下,讓第三方應用去訪問使用者在某網站上的資源(如頭像,使用者暱稱等)

OAuth分為OAuth1.0和OAuth2.0兩個版本,後來隨著OAuth2.0被使用的越來越廣泛,OAuth1.0逐漸退出舞臺(當然仍有少部分系統在使用1.0授權標準)。下面我們圍繞OAuth2進行展開。

OAuth 2.0致力於簡化客戶端開發人員的工作,同時為Web應用程式,桌面應用程式,行動電話和客廳裝置提供特定的授權流程。

我們以部落格園登入為例:

新同學小明想在“部落格園”發表文章,這時候他進入登入頁面,“部落格園”登入首頁需要小明提供使用者名稱和密碼才允許小明登入。

 而由於小明是第一次使用“部落格園”,他對部落格園可能不是很信任,於是他想通過下方的他信任QQ應用直接登入。

來到這裡我們發現,通過授權,“部落格園”將獲得小明在QQ應用中的資源(暱稱,頭像,性別),併成功登入“部落格園”,而並不需要提供使用者名稱和密碼給“部落格園”。

上例中“部落格園”就是所謂的第三方應用,而QQ就是提供受保護資源的某個應用。

OAuth2四個參與角色

1.資源所有者(Resource Owner):資源的擁有者(上例中:小明)
2.資源伺服器(Resource Server):資源所在的伺服器(上例中:QQ應用)
3.授權伺服器(Authorization Server):用於驗證client(第三方應用)的真實性,提供授權碼和令牌token。授權伺服器可單獨部署,也可以和資源伺服器一起部署。
4.第三方應用(Client):訪問受保護資源的客戶端,上例中:部落格園

這裡,很多同學會對“授權伺服器”存在疑惑,上例中部落格園登入流程並未體現授權伺服器啊?我們接著往下看

OAuth2標準授權流程

結合OAuth2經典流程圖,我們一下再來看下小明登入“部落格園”的流程。

(A)第三方應用(client)“部落格園”向(資源擁有者)小明發起授權請求,即小明點選部落格園登入頁中的QQ登入。

(B)QQ登入授權頁面,小明登入自己的QQ賬號,同意授權給“部落格園”,並返回授權許可憑證。

(C)第三方應用(client)“部落格園”拿著步驟(B)獲取的授權許可憑證,向授權伺服器發起請求。

(D)授權伺服器同意第三方應用(client)“部落格園”的請求,並返回一個令牌Token。

(E)第三方應用(client)“部落格園”拿著Token請求(資源伺服器)QQ應用中的暱稱,頭像等資源

(F)(資源伺服器)QQ應用驗證Token通過,並返回資源給第三方應用(client)“部落格園”

通過上述流程,我們可以看出,當QQ授權同意後,並不是馬上就將QQ應用中的資源返回給第三方客戶端的,而是需要客戶端拿著授權許可憑證,向授權伺服器請求並獲取最終的鑰匙Token,然後才能獲得受保護資源。

顯然,授權伺服器充當了一個驗證client,並頒發token令牌的服務角色。

那麼授權許可憑證到底是什麼呢?

OAuth2授權許可憑證

1.授權碼(Authorization Code)

 該模式目前是功能最完整、流程最嚴密的授權模式。一般需要client有專門的後端server,根據獲取的授權碼code在後端server請求令牌token。

上例中部落格園登入授權就是用的“授權碼模式”,互動流程如下:

1)部落格園向小明發起QQ授權請求。

2)QQ授權驗證同意後,根據部落格園提供的Redirect_url返回,並帶上授權碼code

3)部落格園前端根據返回的url獲取授權碼code,並在後端server向授權伺服器發起請求獲取token

4)授權服務驗證通過,並以json格式返回token

5)部落格園再根據token請求QQ應用中獲取受保護資源(頭像,暱稱等)

   步驟(1),授權請求(Authorization Request):

   client需提供如下主要引數:

   1.client_id:必填,第三方應用唯一標識ID

   2.redirect_uri:必填,授權同意後,重定向地址URL

   3.response_type:必填,授權碼模式下固定值為“code”

   4.state:選填,一個狀態碼,可用於防止跨站請求偽造(CSRF)攻擊。客戶端發起授權請求時會生成一個狀態碼與客戶端繫結,授權請求成功後會將該state原樣返回

   5.scope:選填,標識授權範圍

   例如部落格園向QQ發起授權請求:

https://graph.qq.com/oauth2.0/show?which=Login&display=pc
&client_id=101880508
&scope=get_user_info
&response_type=code
&redirect_uri=***
&state=*****

    擴充套件:跨站請求偽造(CSRF)攻擊:使用者登入了A可信網站,認證資訊儲存在瀏覽器cookie中。當使用者訪問攻擊者建立的B網站時,使用者認證資訊仍有效,攻擊者通過在B網站傳送一個偽造的請求提交到A網站伺服器上,讓A網站伺服器誤以為請求來自於自己的網站。  

 步驟(2),授權請求同意後,返回資訊:

  1.code:授權碼

  2.state:原樣返回,client提交的state狀態碼

  步驟(3),根據授權碼code,後端請求token,client需提供如下引數

1.grant_type:必填。授權碼模式,固定值“authorization_code”。

2.code : 必填。授權同意後返回的授權碼。

3.redirect_uri:必填。授權同意後,重定向地址URL

4.client_id:必填。第三方應用唯一標識ID。

5.client_secret:必填。第三方應用授權申請的祕鑰。

 例如:

POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
grant_type=authorization_code
&code=xxxxxxxxxxx
&redirect_uri=https://example-app.com/redirect
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

步驟(4),授權認證通過,返回主要引數。

1.access_token:訪問令牌。

2.refresh_token:重新整理令牌。

3.expires_in:令牌過期時間。

4.token_type:令牌型別。

其中重新整理令牌refresh_token的作用是,當訪問令牌token失效時,無需重新發起授權獲取新的token,根據重新整理令牌直接請求授權服務,就可以獲取新的令牌。

2.隱式許可(Implicit)

 授權碼模式的簡化應用,跳過了獲取授權碼code的過程,直接獲取token。一般用於沒有後端的Client。

 如果部落格園使用該模式,其工作流程:

1)部落格園向小明發起QQ授權請求。

2)QQ授權驗證同意後,根據部落格園提供的Redirect_url返回,並帶上令牌token

3)部落格園再根據token請求QQ應用中獲取受保護資源(頭像,暱稱等)

  步驟(1),授權請求引數和授權碼模式引數一樣,唯一不同的引數是response_type,Implicit模式下固定值為“token”。

  步驟(2),授權請求同意後,返回資訊:

1.access_token:訪問令牌。

2.token_type:令牌型別。

3.expires_in:令牌過期時間。

  注:隱式授權模式頒發的令牌,不提供refresh重新整理令牌

URL格式:

格式:https://a.com/callback#token=ACCESS_TOKEN

注意,令牌的位置是 URL 錨點“#”後面,而不是查詢字串“?”後面,這是因為 OAuth2允許跳轉網址是 HTTP 協議,因此存在"中間人攻擊"的風險,而瀏覽器跳轉時,錨點不會發到伺服器,就減少了洩漏令牌的風險。

3.使用者密碼模式(Resource Owner Password Credentials)

 客戶端提供使用者名稱和密碼,獲取token。前面我們說OAuth2就是為了避免直接提供使用者名稱和密碼給第三方應用程式而誕生,那麼這裡又是怎麼回事呢?

 其實,該授權模式的初衷是為服務自己的應用啟用密碼登入,使用者使用其使用者名稱和密碼登入該服務的網站或本機應用程式,但是絕對不允許第三方應用程式詢問使用者密碼。

 授權請求,提供引數:

1.grant_type:必填。該模式下固定值為“password”。

2.username:必填。使用者登陸名。

3.passward:必填。使用者登陸密碼。

4.scope:可填。表示授權範圍。

5.客戶端認證引數:通常如果授權服務管理系統給客戶端頒發了身份祕鑰資訊(client_id,client_secret),那麼客戶端發起授權請求時需要攜帶引數client_id和client_secret。或在HTTP Basic auth標頭中接受客戶端client_secret和密碼client_secret

   其中,客戶端認證引數:client_id和client_secret主要是用於授權服務驗證客戶端的身份。如果客戶端都沒在授權服務管理系統備案(不需要驗證客戶端身份),那麼授權請求就不需要這兩個引數。備案又是什麼呢?大家請留意文章末尾。

   以postman請求token為例:

   第一種方式:將client_id和client_secret作為請求體引數

 第二種方式:在HTTP Basic auth請求頭中單獨驗證client_secret和client_secret

 

 

 

授權請求同意後,返回主要引數:

1.access_token:訪問令牌。

2.token_type:令牌型別。

3.expires_in:令牌過期時間。

4.scope:授權範圍

4.客戶端模式(Client Credentials)

 當第三方應用程式請求訪問令牌以訪問其自己的資源(而非代表其他使用者去訪問資源)時,使用該模式。此時第三方應用程式將自己當成資源所有者,直接請求授權伺服器獲取令牌token。

 授權請求,提供引數:

1.grant_type:必填。該模式下固定值為“client_credentials”。

2.scope:可填。表示授權範圍。

3.客戶端認證:必填,包含引數client_id和client_secret;或在HTTP Basic auth標頭中接受客戶端
client_secret和密碼client_secret

以postman請求token為例:

第一種方式:將client_id和client_secret作為請求體引數

 第二種方式:在HTTP Basic auth請求頭中單獨驗證client_secret和client_secret

 

 

 

兩種方式效果一致,都是通過client_id和client_secret,讓授權伺服器驗證客戶端的身份。

授權請求同意後,返回引數:

1.access_token:訪問令牌。

2.token_type:令牌型別。

3.expires_in:令牌過期時間。

4.scope:授權範圍

擴充套件:第三方應用發起授權請求時,client_id和client_secret是哪來的呢?

授權服務提供第三方應用的管理:

第三方應用申請令牌之前,都必須先到授權服務系統備案,說明自己的身份,然後會拿到兩個身份識別碼:客戶端 ID(client ID)和客戶端金鑰(client secret)。這是為了防止令牌被濫用,沒有備案過的第三方應用,是不會拿到令牌的。

5.重新整理令牌refresh  token

前面我們說,當授權請求同意後,通常授權伺服器會返回一個重新整理令牌refresh  token給我們(該返回引數非必選的,由授權服務定製,通常推薦返回該引數)。

當訪問令牌access token 過期後,如果重新發起一遍請求令牌的過程顯然有點麻煩,這時候通過refresh token發起一次請求可以直接獲取新的訪問令牌access token。

請求引數:

1.grant_type:必填。固定值為“refresh_token”。

2.refresh_token:必填。

3.scope:可填。表示授權範圍。

4.客戶端認證:通常如果授權服務管理系統給客戶端頒發了身份祕鑰資訊(client_id,client_secret),那麼客戶端發起授權請求時需要攜帶引數client_id和client_secret。或在HTTP Basic auth標頭中接受客戶端client_secret和密碼client_secret。
如果客戶端不需要身份認證,則無需攜帶任何身份認證的資訊

例如:

POST /oauth/token HTTP/1.1
Host: authorization-server.com
 
grant_type=refresh_token
&refresh_token=xxxxxxxxxxx
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx

 

本文,我們結合部落格園授權QQ登入的案例,描述了OAuth2的基本概念,OAuth2的授權流程以及各種授權模式的使用。多動手,多動手,多動手才能加深自己的理解。

附:推薦幾篇值得學習的OAuth2文章

1.OAuth2.0協議標準

2.大神阮一峰的文章:OAuth2.0的四種方式

相關文章