oauth 2.0 是什麼?
oauth 2.0 是一個授權的開放網路標準
角色
Resource Owner:資源所有者,既使用者 User Agent:使用者代理 Authorization Server:既提供第三方登入服務的伺服器 Client:第三方應用,我們的應用就是一個client
客戶端的授權模式型別
- 授權碼模式
- 隱式授權模式
- 密碼模式
- 客戶端模式
授權碼模式
+----------+
| 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.授權請求 比如你想用登入gitlab,想利用三方授權登入,比如谷歌賬號,當你點選谷歌圖示的時候,會首先發起一個授權請求。 會帶上以下引數,向授權伺服器傳送授權請求:
response_type:表示授權型別,必選項
client_id:表示客戶端的ID,必選項,一般是在授權伺服器上申請應用的時候,頒發的
redirect_uri:重定向的 uri
scope:表示申請的許可權範圍
state:表示客戶端的當前狀態,可以是任意值,認證伺服器會原封不動的返回這個值
複製程式碼
下面是一個例子:
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
複製程式碼
授權伺服器驗證該請求,確保所有的引數提交且有效,授權伺服器會引導使用者進入授權頁面
2.當使用者點選確定授權時,授權伺服器會返回授權碼(code)和狀態引數(state),返回請求到相應的回撥地址(redirect_uri)。至此,使用者的主動行為已經結束。 3.第三方應用,也就是我們的客戶端,在拿到授權碼之後會直接向授權伺服器請求訪問令牌,引數如下:
grant_type:授權模式,必選項,值 "authorization_code"
code:從授權伺服器收到的授權碼
redirect_uri:回撥地址
client_id:標識應用ID
client_secret:授權伺服器頒發的金鑰
複製程式碼
授權伺服器驗證通過後會返回如下引數:
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"
}
複製程式碼
至此整個授權碼流程結束。
需要注意的幾個問題
-
重定向 redirect_uri uri 整個授權過程都是一樣的
-
授權過程只是授權: 比如,你想用 "支付寶" 授權登入 "優酷" ,整個授權的過程只是支付寶驗證使用者授權,返回一個 openId 和 支付寶允許給優酷的一些使用者資訊,優化拿到 openId可以直接上傳服務端註冊,至此整個授權流程結束。後面優酷相關聯的資源獲取和授權沒有任何關聯了。優酷會對這個 openId和賬號做唯一對映,這樣下次還用支付寶授權登入,優酷的服務端不會在建立新的賬號給該使用者,即不需要使用者密碼的一鍵登入。
-
為什麼整個授權流程不直接返回 access_token 而是要經過中間步驟先要獲取授權碼 code ,在根據授權碼再次獲取 access_token ? 最直接的答案是安全,code有效期比較短,而且一個code只能換取一次access_token即失效。獲取code的請求是使用者主動授權之後獲取的從這裡截止,使用者的主動行為結束。 獲取引數不包含 appsecret,請求回來的code回立馬再次發起請求,這次請求會帶上appsecret,授權伺服器驗證並返回 access_token
隱式授權模式
+----------+
| 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 |
| |
+---------+
複製程式碼
和 授權碼請求一樣,首先你會發起一個授權請求,驗證引數
- 授權請求:
response_type:表示授權型別,此處固定值為 token
client_id:客戶端標識
redirect_uri:重定向 uri
scope:訪問的許可權範圍
state:表示客戶端的當前狀態,可以是任意值,認證伺服器會原封不動的返回
複製程式碼
客戶端發起 HTTP 請求,如下:
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.授權伺服器會驗證該請求,確保所有的引數已提交且有效。授權伺服器驗證請求引數中的uri和客戶端提交的重定向的uri保持一致。 如果,該請求是有效的,授權伺服器驗證對資源所有者進行身份驗證並展示授權頁面給使用者。 使用者同意授權請求時,授權伺服器回返回如下引數:
access_token:授權伺服器頒發的訪問令牌
token_type:令牌型別
expires_in:過期時間
scope:表示授權許可權範圍
state:表示客戶端的當前狀態,可以是任意值,認證伺服器會原封不動的返回這個值
複製程式碼
這些引數,拼接在重定向 uri 地址的後面
3.第三方應用向資源伺服器發起請求,不包含上一步獲取的access_token 資源伺服器返回一個網頁(通常是帶有嵌入式指令碼的 HTML 文件),其中的指令碼可以提取出令牌,瀏覽器把access_token傳送給客戶端 至此整個簡單授權模式結束。
隱式授權和授權碼授權的區別就是,是直接獲取access_token,不需要經過code步驟。但是獲取的access_token需要資源伺服器的指令碼從第三方代理(既瀏覽器)中提取出來)傳送給客戶端。
密碼模式
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
複製程式碼
這種模式使用者必須把"使用者名稱"和"密碼"給到客戶端,客戶端不得儲存密碼。這通常用在使用者讀客戶端高度信任的情況下。
- 資源所有者像客戶端提供他的使用者名稱和密碼
- 當發起請求客戶端向授權伺服器進行身份認證
- 授權伺服器對客戶端進行身份驗證,驗證資源所有者的憑證,如果有效則頒發訪問令牌。
訪問令牌請求引數:
grant_type:值必須為 "password"
username:資源所有者使用者名稱
password:資源所有者密碼
scope:客戶端授權請求範圍
複製程式碼
例如:
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
複製程式碼
授權伺服器驗證授權請求通過,引數如下:
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 ---------<| |
| | | |
+---------+ +---------------+
複製程式碼
其實,這種模式不屬於授權模式,是客戶端的行為,而非使用者行為 1.客戶端向授權伺服器請求驗證,並要求一個訪問令牌 2.授權伺服器向客戶端進行身份驗證,如果有效,則頒發一個訪問令牌
訪問令牌請求:
grant_type:表示授權型別,值必須為 "client_credentials"
scope:客戶端的授權請求反問
複製程式碼
如下,請求:
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials
複製程式碼
授權伺服器驗證該請求,如果請求有效則返回,令牌:
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"
}
複製程式碼
重新整理訪問令牌
如果,授權伺服器頒發了重新整理 refresh_token,可以在客戶端發起重新整理 access_token,一般 refresh_token有效期是大於access_token有效期 請求引數如下:
grant_type:授權型別,此處必須為 "refresh_token"
refresh_token:頒發給客戶端的重新整理令牌
scope:表示可訪問的許可權範圍
複製程式碼
如下 HTTP 請求
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
複製程式碼