OAuth 2.0 是什么?
OAuth 2.0 是一個(gè)授權(quán)的開放網(wǎng)絡(luò)標(biāo)準(zhǔn)
角色
Resource Owner:資源所有者,既用戶
User Agent:用戶代理
Authorization Server:既提供第三方登錄服務(wù)的服務(wù)器
Client:第三方應(yīng)用,我們的應(yīng)用就是一個(gè)client
客戶端的授權(quán)模式類型
- 授權(quán)碼模式
- 隱式授權(quán)模式
- 密碼模式
- 客戶端模式
授權(quán)碼模式
+----------+
| 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)
1.授權(quán)請(qǐng)求
比如你想登錄gitlab,并且想利用三方授權(quán)登錄,比如谷歌賬號(hào),當(dāng)你點(diǎn)擊谷歌圖標(biāo)的時(shí)候,會(huì)首先發(fā)起一個(gè)請(qǐng)求,會(huì)帶上以下參數(shù),向授權(quán)服務(wù)器發(fā)送授權(quán)請(qǐng)求:
response_type:表示授權(quán)類型,必選項(xiàng)
client_id:表示客戶端的ID,必選項(xiàng),一般是在授權(quán)服務(wù)器上申請(qǐng)應(yīng)用的時(shí)候,頒發(fā)的
redirect_uri:重定向的 uri
scope:表示申請(qǐng)的權(quán)限范圍
state:表示客戶端的當(dāng)前狀態(tài),可以是任意值,認(rèn)證服務(wù)器會(huì)原封不動(dòng)的返回這個(gè)值
下面是一個(gè)例子:
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
授權(quán)服務(wù)器驗(yàn)證該請(qǐng)求,確保所有的參數(shù)提交且有效,授權(quán)服務(wù)器會(huì)引導(dǎo)用戶進(jìn)入授權(quán)頁面
2.當(dāng)用戶點(diǎn)擊確定授權(quán)時(shí),授權(quán)服務(wù)器會(huì)返回授權(quán)碼(code)和狀態(tài)參數(shù)(state),返回請(qǐng)求到相應(yīng)的回調(diào)地址(redirect_uri)。至此,用戶的主動(dòng)行為已經(jīng)結(jié)束。
3.第三方應(yīng)用,也就是我們的客戶端,在拿到授權(quán)碼之后會(huì)直接向授權(quán)服務(wù)器請(qǐng)求訪問令牌,參數(shù)如下:
grant_type:授權(quán)模式,必選項(xiàng),值 "authorization_code"
code:從授權(quán)服務(wù)器收到的授權(quán)碼
redirect_uri:回調(diào)地址
client_id:標(biāo)識(shí)應(yīng)用ID
client_secret:授權(quán)服務(wù)器頒發(fā)的密鑰
授權(quán)服務(wù)器驗(yàn)證通過后會(huì)返回如下參數(shù):
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"
}
至此整個(gè)授權(quán)碼流程結(jié)束。
需要注意的幾個(gè)問題
重定向 redirect_uri uri 整個(gè)授權(quán)過程都是一樣的
授權(quán)過程只是授權(quán):
比如,你想用 "支付寶" 授權(quán)登錄 "優(yōu)酷" ,整個(gè)授權(quán)的過程只是支付寶驗(yàn)證用戶授權(quán),返回一個(gè) openId 和 支付寶允許給優(yōu)酷的一些用戶信息,優(yōu)化拿到 openId可以直接上傳服務(wù)端注冊(cè),至此整個(gè)授權(quán)流程結(jié)束。后面優(yōu)酷相關(guān)聯(lián)的資源獲取和授權(quán)沒有任何關(guān)聯(lián)了。優(yōu)酷會(huì)對(duì)這個(gè) openId和賬號(hào)做唯一映射,這樣下次還用支付寶授權(quán)登錄,優(yōu)酷的服務(wù)端不會(huì)在創(chuàng)建新的賬號(hào)給該用戶,即不需要用戶密碼的一鍵登錄。為什么整個(gè)授權(quán)流程不直接返回 access_token 而是要經(jīng)過中間步驟先要獲取授權(quán)碼 code ,在根據(jù)授權(quán)碼再次獲取 access_token ?
最直接的答案是安全,code有效期比較短,而且一個(gè)code只能換取一次access_token即失效。獲取code的請(qǐng)求是用戶主動(dòng)授權(quán)之后獲取的從這里截止,用戶的主動(dòng)行為結(jié)束。
獲取參數(shù)不包含 appsecret,請(qǐng)求回來的code回立馬再次發(fā)起請(qǐng)求,這次請(qǐng)求會(huì)帶上appsecret,授權(quán)服務(wù)器驗(yàn)證并返回 access_token
隱式授權(quán)模式
+----------+
| 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 |
| |
+---------+
和 授權(quán)碼請(qǐng)求一樣,首先你會(huì)發(fā)起一個(gè)授權(quán)請(qǐng)求,驗(yàn)證參數(shù)
- 授權(quán)請(qǐng)求:
response_type:表示授權(quán)類型,此處固定值為 token
client_id:客戶端標(biāo)識(shí)
redirect_uri:重定向 uri
scope:訪問的權(quán)限范圍
state:表示客戶端的當(dāng)前狀態(tài),可以是任意值,認(rèn)證服務(wù)器會(huì)原封不動(dòng)的返回
客戶端發(fā)起 HTTP 請(qǐng)求,如下:
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
2.授權(quán)服務(wù)器會(huì)驗(yàn)證該請(qǐng)求,確保所有的參數(shù)已提交且有效。授權(quán)服務(wù)器驗(yàn)證請(qǐng)求參數(shù)中的uri和客戶端提交的重定向的uri保持一致。
如果,該請(qǐng)求是有效的,授權(quán)服務(wù)器驗(yàn)證對(duì)資源所有者進(jìn)行身份驗(yàn)證并展示授權(quán)頁面給用戶。
用戶同意授權(quán)請(qǐng)求時(shí),授權(quán)服務(wù)器回返回如下參數(shù):
access_token:授權(quán)服務(wù)器頒發(fā)的訪問令牌
token_type:令牌類型
expires_in:過期時(shí)間
scope:表示授權(quán)權(quán)限范圍
state:表示客戶端的當(dāng)前狀態(tài),可以是任意值,認(rèn)證服務(wù)器會(huì)原封不動(dòng)的返回這個(gè)值
這些參數(shù),拼接在重定向 uri 地址的后面
3.第三方應(yīng)用向資源服務(wù)器發(fā)起請(qǐng)求,不包含上一步獲取的access_token
資源服務(wù)器返回一個(gè)網(wǎng)頁(通常是帶有嵌入式腳本的 HTML 文檔),其中的腳本可以提取出令牌,瀏覽器把a(bǔ)ccess_token發(fā)送給客戶端
至此整個(gè)簡(jiǎn)單授權(quán)模式結(jié)束。
隱式授權(quán)和授權(quán)碼授權(quán)的區(qū)別就是,是直接獲取access_token,不需要經(jīng)過code步驟。但是獲取的access_token需要資源服務(wù)器的腳本從第三方代理(既瀏覽器)中提取出來)發(fā)送給客戶端。
密碼模式
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
這種模式用戶必須把"用戶名"和"密碼"給到客戶端,客戶端不得存儲(chǔ)密碼。這通常用在用戶讀客戶端高度信任的情況下。
- 資源所有者像客戶端提供他的用戶名和密碼
- 當(dāng)發(fā)起請(qǐng)求客戶端向授權(quán)服務(wù)器進(jìn)行身份認(rèn)證
- 授權(quán)服務(wù)器對(duì)客戶端進(jìn)行身份驗(yàn)證,驗(yàn)證資源所有者的憑證,如果有效則頒發(fā)訪問令牌。
訪問令牌請(qǐng)求參數(shù):
grant_type:值必須為 "password"
username:資源所有者用戶名
password:資源所有者密碼
scope:客戶端授權(quán)請(qǐng)求范圍
例如:
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
授權(quán)服務(wù)器驗(yàn)證授權(quán)請(qǐng)求通過,參數(shù)如下:
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"
}
客戶端模式
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
其實(shí),這種模式不屬于授權(quán)模式,是客戶端的行為,而非用戶行為
1.客戶端向授權(quán)服務(wù)器請(qǐng)求驗(yàn)證,并要求一個(gè)訪問令牌
2.授權(quán)服務(wù)器向客戶端進(jìn)行身份驗(yàn)證,如果有效,則頒發(fā)一個(gè)訪問令牌
訪問令牌請(qǐng)求:
grant_type:表示授權(quán)類型,值必須為 "client_credentials"
scope:客戶端的授權(quán)請(qǐng)求反問
如下,請(qǐng)求:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
授權(quán)服務(wù)器驗(yàn)證該請(qǐng)求,如果請(qǐng)求有效則返回,令牌:
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"
}
刷新訪問令牌
如果,授權(quán)服務(wù)器頒發(fā)了刷新 refresh_token,可以在客戶端發(fā)起刷新 access_token,一般 refresh_token有效期是大于access_token有效期
請(qǐng)求參數(shù)如下:
grant_type:授權(quán)類型,此處必須為 "refresh_token"
refresh_token:頒發(fā)給客戶端的刷新令牌
scope:表示可訪問的權(quán)限范圍
如下 HTTP 請(qǐng)求
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA
參考鏈接
http://www.rfcreader.com/#rfc6749