隨著互聯(lián)網(wǎng)的發(fā)展,各種應(yīng)用相互交叉,到處需要用戶登錄,信息安全成為了不可回避的問題,應(yīng)用需要擴(kuò)展,用戶需要更好的體驗(yàn),信息需要更安全的保障,為了滿足這些需求,互聯(lián)網(wǎng)技術(shù)不斷推陳出新,從通信安全,到各種協(xié)議框架,有無(wú)數(shù)的解決方案。
其中 OAuth 框架是很閃耀的一個(gè),一經(jīng)推出,就得到各大互聯(lián)網(wǎng)公司的積極響應(yīng),到2010年推出了 OAuth 2.0, 不但修補(bǔ)了 1.0 的安全漏洞,而且簡(jiǎn)化了授權(quán)流程,得到更廣泛的應(yīng)用,成為了主流。從 PC 到 Web,從移動(dòng)端到物聯(lián)網(wǎng),越來(lái)越多的應(yīng)用構(gòu)建在 OAuth 框架之上,那么 OAuth 是什么呢?
OAuth 是什么
OAuth 框架提供了一種認(rèn)證和授權(quán)機(jī)制,可以讓用戶將其受保護(hù)的資源授權(quán)給其他應(yīng)用來(lái)訪問或者使用。
阮一峰老師對(duì) 授權(quán)有個(gè)形象比喻:授權(quán)機(jī)制相當(dāng)于你給快遞員一個(gè)臨時(shí)密碼(授權(quán)),快遞員可以使用這個(gè)密碼打開小區(qū)門禁,將快遞送到你家門口,而后臨時(shí)密碼將失效(詳見參考鏈接)。
這個(gè)例子中,你就是 用戶
,小區(qū)是 受保護(hù)的資源
,快遞員是其他應(yīng)用(第三方應(yīng)用
)。
如果沒有這個(gè)機(jī)制:
- 要么就得告訴所有可能給你送快遞的快遞員門禁密碼,不安全
有了 OAuth 框架(協(xié)議),既方便,又安全
OAuth2.0
這里不打算介紹 OAuth 1.0,原因是:
- OAuth 1 和 后面的 OAuth 1a 交流流程比較復(fù)雜
- OAuth 2.0 安全性好,應(yīng)用更廣泛
如果想了解更多關(guān)于 OAuth 的知識(shí),請(qǐng)?jiān)L問參考鏈接
角色
OAuth2.0 實(shí)際上就是讓第三方服務(wù)獲得用戶在資源服務(wù)器上的授權(quán)的過程,會(huì)涉及到 4 種角色
- 資源擁有者(
Resource owner
),即用戶 - 認(rèn)證服務(wù)器(
Authorization server
),用來(lái)認(rèn)證用戶憑證,頒布授權(quán)碼的服務(wù)器 - 資源服務(wù)器(
Resource Server
),存放用戶受保護(hù)的資源的服務(wù)器 - 第三方應(yīng)用(
Client
),也稱之為客戶端(后續(xù)皆稱 客戶端),需要得到用戶授權(quán),以便訪問用戶受保護(hù)的資源的應(yīng)用程序
不是任何客戶端都能得到授權(quán)的,在開通 OAuth 授權(quán)之前,需要先到認(rèn)證服務(wù)器或者資源服務(wù)器上注冊(cè),注冊(cè)成功會(huì)得到 appid
和 app_secret
,用來(lái)向認(rèn)證服務(wù)器表明應(yīng)用的身份
角色之間關(guān)聯(lián)
了解了 OAuth2.0 框架中的主要角色,有必要了解下角色之間關(guān)聯(lián)關(guān)系
- 認(rèn)證服務(wù)器 和 資源服務(wù)器:通常來(lái)說(shuō),認(rèn)證服務(wù)器和資源服務(wù)器同屬于一個(gè)服務(wù)商,它們就有天生的關(guān)聯(lián),而且是內(nèi)部的安全的,甚至它們可以部署在同一個(gè)服務(wù)器(Web 服務(wù)器)上
- 用戶 和 服務(wù)商:對(duì)于像 Github、微信這樣的知名應(yīng)用,用戶會(huì)主動(dòng)在這些應(yīng)用或服務(wù)上注冊(cè),填寫的資料信息,設(shè)置的昵稱,產(chǎn)生的文章、上傳的照片,等等將成為用戶的資源,這些資源被存放在資源服務(wù)器上
- 客戶端 和 服務(wù)商:客戶端,即第三方應(yīng)用,要從服務(wù)商的資源服務(wù)器中獲取數(shù)據(jù),給用戶提供額外服務(wù),必須在服務(wù)商處注冊(cè),提供應(yīng)用的基本信息,認(rèn)證信息,服務(wù)域名,申請(qǐng)用戶授權(quán)的范圍、甚至企業(yè)資質(zhì)(例如申請(qǐng)微信公眾號(hào)的企業(yè)服務(wù))等等,申請(qǐng)通過后,服務(wù)商會(huì)返回
appid
和 app_secret
,作為客戶端和服務(wù)商的交互憑證 - 用戶 和 客戶端:客戶端提供了特別的服務(wù),可以吸引到用戶,為了讓用戶體驗(yàn)更好,引導(dǎo)用戶通過授權(quán)的方式,從而獲取授權(quán)范圍內(nèi)的用戶信息,例如 Openid,作為用戶在客戶端上的唯一標(biāo)識(shí),從而建立起和用戶的關(guān)聯(lián)
至此,四個(gè)角色之間的關(guān)聯(lián)就建立好了,下面開始介紹具體的授權(quán)方式
基本授權(quán)流程
沒有比圖更能說(shuō)明白流程的,借用 RFC6749 文檔中的圖:
- A 客戶端,向用戶發(fā)出授權(quán)請(qǐng)求
- B 用戶同意或者拒絕客戶端的授權(quán)請(qǐng)求,假設(shè)是同意
- C 客戶端拿著用戶的授權(quán)請(qǐng)求認(rèn)證服務(wù)器做認(rèn)證
- D 如果 C 通過認(rèn)證,認(rèn)證服務(wù)器將返回
Access Token
,即可以訪問資源的令牌 - E 客戶端使用
Access Token
請(qǐng)求資源服務(wù)器上的資源 - F 資源服務(wù)器驗(yàn)證了
Access Token
后,返回受保護(hù)的資源
流程中最核心的是讓客戶端獲得 Access Token
,之后在訪問受保護(hù)資源時(shí),就不需要用戶反復(fù)授權(quán)了
Access Token
顯然不是用戶在資源服務(wù)器上的密碼,是有認(rèn)證服務(wù)器頒發(fā)的,那么也可以被銷毀
Access Token
和之前課程中的 JWT 是類似的,實(shí)際上 JWT 是 OAuth 認(rèn)證的一個(gè)特例
授權(quán)模式
根據(jù)授權(quán)流程,OAuth2.0 定義了 4 種針對(duì)不同應(yīng)用場(chǎng)景的授權(quán)模式
- 授權(quán)碼模式(authorization code)
- 密碼模式(resource owner password credentials)
- 客戶端模式(client credentials)
授權(quán)碼模式
授權(quán)碼模式是最完整,安全性最高的授權(quán)模式,也是最常用的一種模式,其特點(diǎn)是通過客戶端的后臺(tái)服務(wù)器與認(rèn)證服務(wù)器交互,如圖:
注意:上圖中的步驟 A, B, C 在通過用戶代理端( User-Agent 一般指瀏覽器)時(shí),被拆分成了兩部分
- A 用戶訪問客戶端的客戶端,后者將前者導(dǎo)向認(rèn)證服務(wù)器
- C 假設(shè)用戶給予授權(quán),認(rèn)證服務(wù)器將用戶導(dǎo)向客戶端事先指定的"重定向URI"(Redirection URI),同時(shí)附上一個(gè)授權(quán)碼
- D 客戶端收到授權(quán)碼,通過后臺(tái)服務(wù)器,附上早先的"重定向URI",向認(rèn)證服務(wù)器申請(qǐng)令牌。這一步對(duì)用戶不可見
- E 認(rèn)證服務(wù)器核對(duì)了授權(quán)碼和重定向URI,確認(rèn)無(wú)誤后,向客戶端發(fā)送訪問令牌(access token)和更新令牌(refresh token)
接下來(lái)說(shuō)明一下過程中所包含一下參數(shù)
不同認(rèn)證服務(wù)器上的參數(shù)名稱有可能不同,但含義相同,例如 client_id
一般被 appid
代替
步驟 A,客戶端申請(qǐng)認(rèn)證的 URI,包含以下參數(shù):
response_type
:表示授權(quán)類型,必選項(xiàng),此處的值固定為 code
,因?yàn)樾枰全@取授權(quán)碼client_id
:表示客戶端的ID,必選項(xiàng),是在認(rèn)證服務(wù)器分配給客戶端的id,即 appidredirect_uri
:表示重定向URI,可選項(xiàng)scope
:表示申請(qǐng)的權(quán)限范圍,可選項(xiàng)state
:表示客戶端的當(dāng)前狀態(tài),可以指定任意值,認(rèn)證服務(wù)器會(huì)原封不動(dòng)地返回這個(gè)值
步驟 C,認(rèn)證服務(wù)器回應(yīng)的 URI,包含以下參數(shù):
code
:表示授權(quán)碼,必選項(xiàng)。該碼的有效期應(yīng)該很短,通常設(shè)為 10 分鐘,客戶端只能使用該碼一次,否則會(huì)被認(rèn)證服務(wù)器拒絕。該碼與客戶端 ID 和重定向 URI,是一一對(duì)應(yīng)關(guān)系state
:如果客戶端的請(qǐng)求中包含這個(gè)參數(shù),認(rèn)證服務(wù)器的回應(yīng)也必須一模一樣包含這個(gè)參數(shù)
步驟 D,客戶端向認(rèn)證服務(wù)器申請(qǐng)令牌的 HTTP 請(qǐng)求,包含以下參數(shù):
grant_type
:表示使用的授權(quán)模式,必選項(xiàng),此處的值固定為 authorization_code
code
:表示上一步獲得的授權(quán)碼,必選項(xiàng)redirect_uri
:表示重定向 URI,必選項(xiàng),且必須與 A 步驟中的該參數(shù)值保持一致client_id
:表示客戶端 ID,必選項(xiàng)
步驟 E,認(rèn)證服務(wù)器發(fā)送的 HTTP 響應(yīng),包含以下參數(shù):
access_token
:表示訪問令牌,必選項(xiàng)token_type
:表示令牌類型,該值大小寫不敏感,必選項(xiàng),可以是bearer類型或mac類型expires_in
:表示過期時(shí)間,單位為秒。如果省略該參數(shù),必須其他方式設(shè)置過期時(shí)間refresh_token
:表示更新令牌,用來(lái)獲取下一次的訪問令牌,可選項(xiàng)scope
:表示權(quán)限范圍,如果與客戶端申請(qǐng)的范圍一致,此項(xiàng)可省略
認(rèn)證服務(wù)器會(huì)以 JSON 的形式返回 access_token
數(shù)據(jù),且不允許做緩存,以提高安全性
簡(jiǎn)化模式
簡(jiǎn)化模式不需要通過客戶端后臺(tái)服務(wù)器,直接在瀏覽器中向認(rèn)證服務(wù)器申請(qǐng)令牌,跳過了授權(quán)碼
這個(gè)步驟,因此得名。所有步驟在瀏覽器中完成,令牌對(duì)訪問者是可見的,且客戶端不需要認(rèn)證,如圖:
- A 客戶端將用戶導(dǎo)向認(rèn)證服務(wù)器
- C 假設(shè)用戶給予授權(quán),認(rèn)證服務(wù)器將用戶導(dǎo)向客戶端指定的
重定向URI
,并在URI的Hash部分包含了訪問令牌 access_token
- D 瀏覽器向資源服務(wù)器發(fā)出請(qǐng)求,其中不包括上一步收到的Hash值
- E 資源服務(wù)器返回一個(gè)網(wǎng)頁(yè),其中包含的代碼可以獲取Hash值中的令牌
- F 瀏覽器執(zhí)行上一步獲得的腳本,提取出令牌
接下來(lái)說(shuō)明一下過程中所包含一下參數(shù)
步驟 A,客戶端發(fā)送 HTTP 請(qǐng)求,包含的參數(shù):
response_type
:表示授權(quán)類型,此處的值固定為 token
,必選項(xiàng)client_id
:表示客戶端的ID,必選項(xiàng)redirect_uri
:表示重定向的URI,可選項(xiàng)scope
:表示權(quán)限范圍,可選項(xiàng)state
:表示客戶端的當(dāng)前狀態(tài),可以指定任意值,認(rèn)證服務(wù)器會(huì)原封不動(dòng)地返回這個(gè)值
步驟 C,認(rèn)證服務(wù)器回應(yīng)客戶端的 URI,包含以下參數(shù):
access_token
:表示訪問令牌,必選項(xiàng)。token_type
:表示令牌類型,該值大小寫不敏感,必選項(xiàng)expires_in
:表示過期時(shí)間,單位為秒。如果省略該參數(shù),必須其他方式設(shè)置過期時(shí)間scope
:表示權(quán)限范圍,如果與客戶端申請(qǐng)的范圍一致,此項(xiàng)可省略state
:如果客戶端的請(qǐng)求中包含這個(gè)參數(shù),認(rèn)證服務(wù)器的回應(yīng)也必須一模一樣包含這個(gè)參數(shù)
有個(gè)需要注意的地方,步驟 C,返回的 access_token 放在重定向 URL 的 Fragment 中,即錨點(diǎn)中, # 后面,例如
http:///cb#access_token=2YotnFZFEjr1zCsicMWpAA
&state=xyz&token_type=example&expires_in=3600
因?yàn)殄^點(diǎn)中的內(nèi)容不會(huì)發(fā)送給后臺(tái),從而減少了一次數(shù)據(jù)傳輸,降低了一定風(fēng)險(xiǎn)
對(duì)于簡(jiǎn)化模式獲得的 access_token 有效期很短,一般是會(huì)話級(jí)的,即當(dāng)會(huì)話結(jié)束時(shí)就失效
密碼模式
密碼模式中,用戶向客戶端提供自己的用戶名和密碼,客戶端使用這些信息,向"服務(wù)商提供商"索要授權(quán)。
在這種模式中,用戶必須把自己的密碼給客戶端,但是客戶端不得儲(chǔ)存密碼(既然用戶信任你,你就必須兌現(xiàn)這個(gè)承諾)。
密碼模式的特性決定,需要用在用戶對(duì)客戶端高度信任的情況下,比如客戶端是操作系統(tǒng)的一部分,或者由一個(gè)著名公司出品,而認(rèn)證服務(wù)器只有在其他授權(quán)模式無(wú)法執(zhí)行的情況下,才能考慮使用這種模式
這里只簡(jiǎn)單介紹下,不做做詳細(xì)講解,如有興趣了解,可以查閱文末參考
客戶端模式
客戶端模式指客戶端以自己的名義,而不是以用戶的名義,向"服務(wù)提供商"進(jìn)行認(rèn)證。嚴(yán)格地說(shuō),客戶端模式并不屬于 OAuth 框架所要解決的問題
在這種模式中,用戶直接向客戶端注冊(cè),客戶端以自己的名義要求"服務(wù)提供商"提供服務(wù),其實(shí)不存在授權(quán)問題
客戶端模式,就像二道販子(只為借用比喻,并無(wú)貶義),將原始服務(wù)包裝后,再提供給最終用戶,常見于多租戶的 Saas 系統(tǒng),例如統(tǒng)一提供支付通道、處理 GPS 信息等
這里也只簡(jiǎn)單介紹下,如有興趣了解,可以查閱文末參考
刷新 access_token
在 授權(quán)碼模式中,授權(quán)服務(wù)器可以會(huì)同時(shí)返回 refresh_token
,用來(lái)在 access_token
過期前,重新獲取新的access_token
,不需要用戶重新確認(rèn)授權(quán),有助于提高用戶體驗(yàn)
在 access_token 過期前,客戶端可用 refresh_token 向授權(quán)服務(wù)器發(fā)送請(qǐng)求,例如,假設(shè) 是授權(quán)服務(wù)器地址,請(qǐng)求大體是:
https:///oauth/token?
grant_type=refresh_token&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
refresh_token=REFRESH_TOKEN
grant_type
: 授權(quán)類型,值為 'refresh_token'client_id
: 客戶端 id,即第三方應(yīng)用在授權(quán)服務(wù)器上注冊(cè)被分配的 idclient_secret
: 客戶端和授權(quán)服務(wù)器通行的密鑰,由授權(quán)服務(wù)器頒發(fā),在特殊需要確認(rèn)的情況下需要作為驗(yàn)證條件refresh_token
: 用戶獲取新的 access_token
的 refresh_token
安全的 OAuth2.0
上面較為詳細(xì)的講述了 OAuth2.0 框架,了解了在開放網(wǎng)絡(luò)中如何安全的獲取用戶授權(quán)的技術(shù)細(xì)節(jié),但再完善的交互方案、再?gòu)?fù)雜的嚴(yán)密的通信過程,都避免不了中間人攻擊,當(dāng)客戶端和認(rèn)證服務(wù)器之間通過 http 協(xié)議交互數(shù)據(jù)時(shí),會(huì)被截取通信內(nèi)容,從而獲得用戶的授權(quán),這是不能接受的,所以 OAuth2.0 需要建立在 https 協(xié)議上,將通信內(nèi)容加密,最大程度的防止信息被竊取。
如果 OAuth2.0 框架用在敏感信息交互上時(shí),必須使用 https 協(xié)議確保安全,但并不是說(shuō)只能支持 https,對(duì)于非敏感數(shù)據(jù),或者不重要的授權(quán),可以使用 http 協(xié)議作為通信方式
總結(jié)
本節(jié)課程著重介紹了 OAuth2.0 授權(quán)框架,從它的作用,到具體的技術(shù)細(xì)節(jié),做了較為詳細(xì)的講述,如果需要用安全的授權(quán),需要將通信建立在 https 協(xié)議之上。由于 OAuth 概念較多,流程復(fù)雜,這節(jié)沒有涉及到具體的編程實(shí)踐,下一節(jié),我們以 Github 為例,使用之前介紹過的 Authlib 模塊,用 Flash 實(shí)現(xiàn)一個(gè)第三方應(yīng)用,作為實(shí)踐,敬請(qǐng)期待
參考
- RFC 6749: https://tools./html/rfc6749
- 比喻: http://www./blog/2019/04/oauth_design.html
- http://www./blog/2014/05/oauth_2_0.html
- https://docs./en/stable/basic/oauth2.html