概述
OAuth 2.0用來授權(quán)第三方應(yīng)用,獲取用戶數(shù)據(jù)。
OAuth 2.0的由來
- 引出問題
思考一個(gè)問題?外賣配送員問題?。?!
我住在一個(gè)大型的有門禁系統(tǒng)的居民小區(qū),由于外賣小哥沒有該小區(qū)的秘密,必須得找到一個(gè)辦法,讓外賣小哥通過門禁系統(tǒng),進(jìn)入小區(qū)。如果我把自己的密碼,告訴外賣小哥,他就擁有了與我同樣的權(quán)限。假如我想取消他進(jìn)入小區(qū)的權(quán)力,我自己的密碼也得跟著改了,還得通知其他的外賣小哥,并且我也不確定要告訴多少外賣小哥。 - 授權(quán)機(jī)制的設(shè)計(jì)
- 門禁系統(tǒng)增加一個(gè)按鈕,叫做"獲取授權(quán)"。外賣小哥需要首先按這個(gè)按鈕,去申請授權(quán)。
- 他按下按鈕以后,屋主的手機(jī)就會跳出對話框:有人正在要求授權(quán)。系統(tǒng)還會顯示該外賣小哥的姓名、工號和所屬的公司。
- 我確認(rèn)請求屬實(shí),就點(diǎn)擊按鈕,告訴門禁系統(tǒng),我同意給予他進(jìn)入小區(qū)的授權(quán)。
- 門禁系統(tǒng)得到我的確認(rèn)以后,向快遞員顯示一個(gè)進(jìn)入小區(qū)的令牌(access token)。令牌就是類似密碼的一串?dāng)?shù)字,只在短期內(nèi)(比如七天)有效。
- 快遞員向門禁系統(tǒng)輸入令牌,進(jìn)入小區(qū)。
- 互聯(lián)網(wǎng)場景
技術(shù)來源于生活,以上案例就是Oauth的設(shè)計(jì)了。- 居民小區(qū)就是儲存用戶數(shù)據(jù)的網(wǎng)絡(luò)服務(wù)。比如,微信支付寶儲存了我的好友信息,獲取這些信息,就必須經(jīng)過微信支付寶的"門禁系統(tǒng)"。
- 外賣小哥就是第三方應(yīng)用,想要穿過門禁系統(tǒng),進(jìn)入小區(qū)。
- 最后,我就是用戶本人,同意授權(quán)第三方應(yīng)用進(jìn)入小區(qū),獲取我的數(shù)據(jù)。
總結(jié):OAuth 就是一種授權(quán)機(jī)制。數(shù)據(jù)的所有者告訴系統(tǒng),同意授權(quán)第三方應(yīng)用進(jìn)入系統(tǒng),獲取這些數(shù)據(jù)。系統(tǒng)從而產(chǎn)生一個(gè)短期的進(jìn)入令牌(token),用來代替密碼,供第三方應(yīng)用使用。
- 令牌與密碼
令牌(token)與密碼(password)的作用是一樣的,都可以進(jìn)入系統(tǒng),但是有三點(diǎn)差異。- 令牌是短期的,到期會自動失效,用戶自己無法修改。密碼一般長期有效,用戶不修改,就不會發(fā)生變化。
- 令牌可以被數(shù)據(jù)所有者撤銷,會立即失效。以上例而言,屋主可以隨時(shí)取消快遞員的令牌。密碼一般不允許被他人撤銷。
- 令牌有權(quán)限范圍(scope),比如只能進(jìn)小區(qū)的二號門。對于網(wǎng)絡(luò)服務(wù)來說,只讀令牌就比讀寫令牌更安全。密碼一般是完整權(quán)限。
總結(jié):上面這些設(shè)計(jì),保證了令牌既可以讓第三方應(yīng)用獲得權(quán)限,同時(shí)又隨時(shí)可控,不會危及系統(tǒng)安全。這就是 OAuth 2.0 的優(yōu)點(diǎn)。
OAuth 2.0 的四種方式
- RFC 6749
OAuth 2.0 的標(biāo)準(zhǔn)是 RFC 6749 文件。該文件先解釋了 OAuth 是什么。
OAuth 引入了一個(gè)授權(quán)層,用來分離兩種不同的角色:客戶端和資源所有者。資源所有者同意以后,資源服務(wù)器可以向客戶端頒發(fā)令牌,OAuth 的核心就是向第三方應(yīng)用頒發(fā)令牌??蛻舳送ㄟ^令牌,去請求數(shù)據(jù)。由于互聯(lián)網(wǎng)有多種場景,該標(biāo)準(zhǔn)定義了獲得令牌的四種授權(quán)方式(authorization grant )。不管哪一種授權(quán)方式,第三方應(yīng)用申請令牌之前,都必須先到系統(tǒng)備案,說明自己的身份,然后會拿到兩個(gè)身份識別碼:客戶端 ID(client ID)和客戶端密鑰(client secret)。這是為了防止令牌被濫用,沒有備案過的第三方應(yīng)用,是不會拿到令牌的。 - 授權(quán)碼方式授權(quán)
授權(quán)碼(authorization code)方式,指的是第三方應(yīng)用先申請一個(gè)授權(quán)碼,然后再用該碼獲取令牌。
這種方式是最常用的流程,安全性也最高,它適用于那些有后端的 Web 應(yīng)用。授權(quán)碼通過前端傳送,令牌則是儲存在后端,而且所有與資源服務(wù)器的通信都在后端完成。這樣的前后端分離,可以避免令牌泄漏。- A 網(wǎng)站提供一個(gè)鏈接,用戶點(diǎn)擊后就會跳轉(zhuǎn)到 B 網(wǎng)站,授權(quán)用戶數(shù)據(jù)給 A 網(wǎng)站使用。下面就是 A 網(wǎng)站跳轉(zhuǎn) B 網(wǎng)站的一個(gè)示意鏈接。
https://b.com/oauth/authorize?
response_type=code&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
上面 URL 中,response_type參數(shù)表示要求返回授權(quán)碼(code),client_id參數(shù)讓 B 知道是誰在請求,redirect_uri參數(shù)是 B 接受或拒絕請求后的跳轉(zhuǎn)網(wǎng)址,scope參數(shù)表示要求的授權(quán)范圍(這里是只讀)。
2. 用戶跳轉(zhuǎn)后,B 網(wǎng)站會要求用戶登錄,然后詢問是否同意給予 A 網(wǎng)站授權(quán)。用戶表示同意,這時(shí) B 網(wǎng)站就會跳回redirect_uri參數(shù)指定的網(wǎng)址。跳轉(zhuǎn)時(shí),會傳回一個(gè)授權(quán)碼。
https://a.com/callback?code=AUTHORIZATION_CODE
- A網(wǎng)站拿到授權(quán)碼以后,就可以在后端,向 B 網(wǎng)站請求令牌。
https://b.com/oauth/token?
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
grant_type=authorization_code&
code=AUTHORIZATION_CODE&
redirect_uri=CALLBACK_URL
上面 URL 中,client_id參數(shù)和client_secret參數(shù)用來讓 B 確認(rèn) A 的身份(client_secret參數(shù)是保密的,因此只能在后端發(fā)請求),grant_type參數(shù)的值是AUTHORIZATION_CODE,表示采用的授權(quán)方式是授權(quán)碼,code參數(shù)是上一步拿到的授權(quán)碼,redirect_uri參數(shù)是令牌頒發(fā)后的回調(diào)網(wǎng)址。
- B網(wǎng)站收到請求以后,就會頒發(fā)令牌。具體做法是向redirect_uri指定的網(wǎng)址,發(fā)送一段 JSON 數(shù)據(jù)。access_token字段就是令牌,A 網(wǎng)站在后端拿到了。
{
"access_token":"ACCESS_TOKEN",
"token_type":"bearer",
"expires_in":2592000,
"refresh_token":"REFRESH_TOKEN",
"scope":"read",
"uid":100101,
"info":{...}
}
- 隱藏式方式授權(quán)
有些 Web 應(yīng)用是純前端應(yīng)用,沒有后端。這時(shí)就不能用上面的方式了,必須將令牌儲存在前端。RFC 6749 規(guī)定了第二種方式,允許直接向前端頒發(fā)令牌。這種方式?jīng)]有授權(quán)碼這個(gè)中間步驟,所以稱為(授權(quán)碼)"隱藏式"(implicit)。
- A 網(wǎng)站提供一個(gè)鏈接,要求用戶跳轉(zhuǎn)到 B 網(wǎng)站,授權(quán)用戶數(shù)據(jù)給 A 網(wǎng)站使用。
https://b.com/oauth/authorize?
response_type=token&
client_id=CLIENT_ID&
redirect_uri=CALLBACK_URL&
scope=read
上面 URL 中,response_type參數(shù)為token,表示要求直接返回令牌。
- 用戶跳轉(zhuǎn)到 B 網(wǎng)站,登錄后同意給予 A 網(wǎng)站授權(quán)。這時(shí),B 網(wǎng)站就會跳回redirect_uri參數(shù)指定的跳轉(zhuǎn)網(wǎng)址,并且把令牌作為 URL 參數(shù),傳給 A 網(wǎng)站。 URL 中,token參數(shù)就是令牌,A 網(wǎng)站因此直接在前端拿到令牌。
https://a.com/callback#token=ACCESS_TOKEN
注意:令牌的位置是 URL 錨點(diǎn)(fragment),而不是查詢字符串(querystring),這是因?yàn)?OAuth 2.0 允許跳轉(zhuǎn)網(wǎng)址是 HTTP 協(xié)議,因此存在"中間人攻擊"的風(fēng)險(xiǎn),而瀏覽器跳轉(zhuǎn)時(shí),錨點(diǎn)不會發(fā)到服務(wù)器,就減少了泄漏令牌的風(fēng)險(xiǎn)。這種方式把令牌直接傳給前端,是很不安全的。因此,只能用于一些安全要求不高的場景,并且令牌的有效期必須非常短,通常就是會話期間(session)有效,瀏覽器關(guān)掉,令牌就失效了。
- 密碼式方式授權(quán)
如果你高度信任某個(gè)應(yīng)用,RFC 6749 也允許用戶把用戶名和密碼,直接告訴該應(yīng)用。該應(yīng)用就使用你的密碼,申請令牌,這種方式稱為"密碼式"(password)。
- A 網(wǎng)站要求用戶提供 B 網(wǎng)站的用戶名和密碼。拿到以后,A 就直接向 B 請求令牌。grant_type參數(shù)是授權(quán)方式,這里的password表示"密碼式",username和password是 B 的用戶名和密碼。
https://oauth.b.com/token?
grant_type=password&
username=USERNAME&
password=PASSWORD&
client_id=CLIENT_ID
- B 網(wǎng)站驗(yàn)證身份通過后,直接給出令牌。注意,這時(shí)不需要跳轉(zhuǎn),而是把令牌放在 JSON 數(shù)據(jù)里面,作為 HTTP 回應(yīng),A 因此拿到令牌。
這種方式需要用戶給出自己的用戶名/密碼,顯然風(fēng)險(xiǎn)很大,因此只適用于其他授權(quán)方式都無法采用的情況,而且必須是用戶高度信任的應(yīng)用。
- 憑證式方式授權(quán)
最后一種方式是憑證式(client credentials),適用于沒有前端的命令行應(yīng)用,即在命令行下請求令牌。grant_type參數(shù)等于client_credentials表示采用憑證式,client_id和client_secret用來讓 B 確認(rèn) A 的身份。
- A 應(yīng)用在命令行向 B 發(fā)出請求。
https://oauth.b.com/token?
grant_type=client_credentials&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET
2.B 網(wǎng)站驗(yàn)證通過以后,直接返回令牌。
這種方式給出的令牌,是針對第三方應(yīng)用的,而不是針對用戶的,即有可能多個(gè)用戶共享同一個(gè)令牌。
- 令牌的使用
A網(wǎng)站拿到令牌以后,就可以向 B網(wǎng)站的 API 請求數(shù)據(jù)了。此時(shí),每個(gè)發(fā)到 API 的請求,都必須帶有令牌。具體做法是在請求的頭信息,加上一個(gè)Authorization字段,令牌就放在這個(gè)字段里面。
curl -H "Authorization: Bearer ACCESS_TOKEN" \
"https://api.b.com"
- 更新令牌
令牌的有效期到了,如果讓用戶重新走一遍上面的流程,再申請一個(gè)新的令牌,很可能體驗(yàn)不好,而且也沒有必要。OAuth 2.0 允許用戶自動更新令牌。
具體方法是,B 網(wǎng)站頒發(fā)令牌的時(shí)候,一次性頒發(fā)兩個(gè)令牌,一個(gè)用于獲取數(shù)據(jù),另一個(gè)用于獲取新的令牌(refresh token 字段)。令牌到期前,用戶使用 refresh token 發(fā)一個(gè)請求,去更新令牌。grant_type參數(shù)為refresh_token表示要求更新令牌,client_id參數(shù)和client_secret參數(shù)用于確認(rèn)身份,refresh_token參數(shù)就是用于更新令牌的令牌。
https://b.com/oauth/token?
grant_type=refresh_token&
client_id=CLIENT_ID&
client_secret=CLIENT_SECRET&
refresh_token=REFRESH_TOKEN
B 網(wǎng)站驗(yàn)證通過以后,就會頒發(fā)新的令牌。