引言
關於應用系統用户身份管理需求,包括身份認證、權限授權、單點登錄、聯合身份認證等業務場景,業界有一堆的標準和規範,比如單點登錄的CAS、Kerberos,第三方身份認證OpenID,第三方用户授權OAuth,聯合身份認證和授權數據標準SAML等。每種技術有各自的應用場景,也存在交叉場景,想要把他們搞清楚,需要了解各種技術的工作原理和應用場景。今天就從其中一個技術開始,對OAuth2.0用户授權框架做一個簡單介紹,想對框架全面瞭解的可以參考框架的標準 RFC6749。
OAuth概述
引用維基百科介紹,OAuth主要的應用場景為:
開放授權(OAuth)是一個開放標準,允許用户讓第三方應用訪問該用户在某一網站上存儲的私密的資源(如照片,視頻,聯繫人列表),而無需將用户名和密碼提供給第三方應用。
舉一個通俗的例子,用户把照片、視頻、聯繫人數據存儲在內容託管雲服務R(Resource)中的Picture、Video、Contact三個模塊中;用户使用在線照片打印服務P(Printer),用户需要讓P服務讀取R服務中的照片進行打印,但不想讓P服務讀取R服務中的其他數據。
傳統方式下,用户只能向P服務提供R服務的用户名密碼,P服務通過用户名密碼登錄R服務,讀取照片,並且不能限制P服務讀取的範圍。
使用OAuth框架,通過以下授權流程,在不暴露用户密碼的情況下,向P服務授予有限的操作S服務的權限,整體流程如下:
- 用户登錄P服務,點擊獲取R服務權限的鏈接。
- 瀏覽器跳轉到R服務,用户登錄R服務後,跳出向P服務授予權限的界面。
- 用户選擇授予Picture模塊、只讀、有效期1小時三個權限的授權選項,並提交。
- 頁面跳轉回P服務,並攜帶R服務生產的授權碼(Picture模塊只讀權限)。
- P服務獲得授權碼,通過授權碼(附加clint_id和client_secret)向R服務發起讀取Picture模塊請求。
- R服務驗證用户信息和授權碼後,向P服務提供Picture的讀取權限。
OAuth發展至今,共有三個版本,分別為:初始化版本OAuth1.0;漏洞修復版本OAuth 1.0a;不向後兼容的OAuth2.0版本。
2.0版本主要是修復了前面版本的安全漏洞,對授權的流程進行了優化,提供了更豐富的使用場景,由於優化精簡了授權的步驟,所以不能向後兼容。本文重點介紹OAuth2.0的業務流程,為便於描述,下文如果沒有特殊説明OAuth指的就是OAuth2.0。
服務角色
根據RFC描述,定義了4中服務角色,分別描述如下:
- 資源所有者 Resource Owner
能夠授予對受保護資源的訪問權限的實體,當資源所有者是人員時,資源所有者就是最終用户。
- 資源服務器 Resource Server
託管受保護資源的服務器,能夠使用訪問令牌(Access Token)接受和響應受保護的資源請求。
- 客户端 Client
代表資源所有者,經其授權後向受保護資源發起請求的應用程序。
- 授權服務器 Authorization Server
授權服務器對資源所有者進行認證並獲取授權後,向客户端頒發訪問令牌(Access Token)
協議運行流程
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
上圖描述了協議定義的四中角色的交互關係和流程的步驟。
A) 客户端向資源所有者申請授予資源訪問權限。如示例中的P服務授予授權界面。
B) 資源所有者向客户端授予資源訪問授權。如示例中的用户授予予Picture模塊、只讀、有效期1小時三個權限的授權選項,並把授予的權限憑證返回給客户端。根據使用場景,Oauth規範定義了四中權限授予類型,具體在下文描述。
C) 客户端向授權服務器提交授權憑證,申請獲取授權令牌。
D) 授權服務器校驗憑證,並向客户端返回授權令牌。
E) 客户端向資源服務器提交授權令牌,申請訪問資源。
F) 資源服務器校驗令牌合法性,並向客户端提供資源服務。
客户端註冊
客户端在申請授權服務前,需要先到認證服務器上註冊。以GitHub作為授權服務器為例,用户先到Register a new OAuth application註冊一個第三方應用。註冊後,系統為應用生成Client ID和Client Secret兩個參數,用户後續的授權。
註冊頁面
註冊結果
權限授予類型
授權授予是表示資源所有者授權客户端的權限憑據,客户端使用此憑證獲取訪問令牌。規範定義了四種授權類型:授權代碼模式(Authorization code)、隱含模式(Implicit)、資源所有者密碼憑據模式(Resource Owner Password)和客户端憑據模式(Client Credentials),概述如下。
授權代碼模式(Authorization code)
授權代碼模式是功能最完善,流程最嚴密,安全性最好的授權模式。這種模式和其他模式的最大區別,是授權過程中,由客户端的後台服務器端參與,即要求客户端是有後台服務器端處理能力的服務,如Web服務。
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
需要特別説明下,Client以Web服務為例,這裏的Client是指Client的後台服務器端,User-Agent是指瀏覽器,授權的流程如下。
A) 用户打開瀏覽器,訪問Client的Web頁面,點擊獲取授權鏈接;瀏覽器把用户重定向到授權服務器。
B) 用户通過認證憑證(用户名密碼)登錄授權服務器。
C) 用户在授權服務器授權頁面選擇要授權給Client訪問的權限並提交;提交後,授權服務器生成授權代碼,把瀏覽器重定向到附帶授權代碼的Client指定的頁面;Client頁面將授權代碼提交到自己的後台服務器端。
D) Client後台服務把授權代碼+client_id+client_secret(後兩者在客户端向授權服務器註冊時生成)提交給授權服務器進行驗證。
E) 授權服務器驗證通過後,向Client返回Access Token和Refresh Token(可選)。
之後,Client就可以拿個Access Token,向資源服務器請求獲取資源的服務。
下面在介紹下各步驟的請求和返回內容。
獲取授權碼請求
請求示例:
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com
參數説明:
response_type,必填。固定為code。client_id,必填。Client向授權服務器註冊時生成的ID。redirect_uri,可選。授權成功後返回授權代碼的重定向地址,此地址也可以在授權服務器上註冊。scope,可選。指定訪問請求的範圍,含義由授權服務器設計。state,推薦。客户端用來在請求和回調之間保持狀態的不透明值。授權服務器在將user-agent重定向回客户端時包含此值。參數用於防止跨站請求偽造。
獲取授權代碼響應
如果授權成功,則授權服務器通過URL重定向向Client返回授權代碼。
響應示例:
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
&state=xyz
參數説明:
-
code,必填。授權成功後授權服務器生成的授權代碼,基於安全性考慮,授權代碼由如下要求- 授權代碼需要設置較短的過期時間,以減輕授權代碼泄露的風險,一般推薦10分鐘。
- 授權代碼只能使用一次,如果多次使用,則此授權代碼相關的所有Token都會被撤銷。
state。和Request中的參數對應。
獲取Access Token請求
請求示例:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
參數説明:
grant_type,必填。固定為authorization_code。code,必填。授權代碼。redirect_uri。和獲取授權碼請求地址一致。client_id,必填。客户端的client_id。
關於client_secret:
根據網上其他資料描述,此請求request-body中還需要攜帶client_secret參數,以保證授權的安全性。但是根據RFC規範的描述,並不推薦使用此方式,而是推薦使用瀏覽器的HTTP Basic authentication認證方案,如上例所示:把認證信息(client_id:client_secret)Base64編碼後放在HTTP頭的Authorization字段中。
獲取Access Token響應
響應示例:
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"
}
參數説明:
- access_token,必填。訪問令牌。
- token_type,必填。令牌類型,可以是bearer類型或mac類型。
- expires_in。過期時間,單位為秒。如果省略該參數,必須其他方式設置過期時間。
- refresh_token,可選。更新令牌,用與獲取下一次的訪問令牌。
- scope,可選。權限範圍,客户端申請填寫的一致。
隱含模式(Implicit)
隱含模式(implicit grant type)授權過程不需要Client後台服務器端參與,直接在瀏覽器中向授權服務器申請令牌,跳過了"授權碼"這個步驟。所有步驟在瀏覽器中完成,令牌對訪問者是可見的,且客户端不需要認證。
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
資源所有者密碼憑據模式(Resource Owner Password)
資源所有者密碼憑據模式中,用户向客户端提供自己的用户名和密碼。客户端使用這些信息,向授權服務器索要授權。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
客户端憑據模式(Client Credentials)
當客户端請求訪問其控制下的受保護資源時,客户端只能使用其客户端憑據(或其他支持的身份驗證方法)來請求訪問令牌。或者其他資源擁有者之前與授權服務器安排好的資源擁有者(其方法不在本文的範圍之內)。客户端憑據授予類型必須僅供機密客户端使用。
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
刷新令牌Refresh Token
在授權代碼模式和資源所有者密碼憑據模式中,獲取Access Token的過程中會返回可選的Refresh Token,Refresh Token用於獲取Access Token。由於Access Token需包含在每個和資源服務器的請求中,使用頻率高,所以應該設置較短的有效期,以減少泄露帶來的風險,當Access Token過期是,可以通過Refresh Token中授權服務器中換取新的Access Token。
刷新過期的Access Token的流程如下。
+--------+ +---------------+
| |--(A)------- Authorization Grant --------->| |
| | | |
| |<-(B)----------- Access Token -------------| |
| | & Refresh Token | |
| | | |
| | +----------+ | |
| |--(C)---- Access Token ---->| | | |
| | | | | |
| |<-(D)- Protected Resource --| Resource | | Authorization |
| Client | | Server | | Server |
| |--(E)---- Access Token ---->| | | |
| | | | | |
| |<-(F)- Invalid Token Error -| | | |
| | +----------+ | |
| | | |
| |--(G)----------- Refresh Token ----------->| |
| | | |
| |<-(H)----------- Access Token -------------| |
+--------+ & Optional Refresh Token +---------------+