1.前言
前段時(shí)間公司取消了Android崗,在面臨失業(yè)沒(méi)有飯碗天天吃土的壓迫下,我轉(zhuǎn)崗變成JAVA工程師。雖然JAVA并不陌生,但在陌生技術(shù)的面前,還是向大佬們低下了頭。
也有人會(huì)問(wèn)為啥不換份工作。自己也問(wèn)過(guò)自己為什么?這份工作掙得少,變化多,不穩(wěn)定。但是卻能每次讓我面臨新的挑戰(zhàn),不論是轉(zhuǎn)崗JAVA還是前端,就是這種挑戰(zhàn)的感覺(jué)讓我無(wú)法停止,如果有一天公司不會(huì)再讓我吸收到養(yǎng)分,那應(yīng)該就是我離開(kāi)的一天。
不多廢話了,因?yàn)樽罱獙?duì)接另一個(gè)公司的接口,面臨權(quán)限的問(wèn)題,領(lǐng)導(dǎo)讓我調(diào)研了下,跟大家也分享下。

2.概念
2.1 OAuth2.0是什么?
OAuth是一個(gè)關(guān)于授權(quán)(authorization)的開(kāi)放網(wǎng)絡(luò)標(biāo)準(zhǔn),在全世界得到廣泛應(yīng)用,目前的版本是2.0版。
OAuth2.0是OAuth協(xié)議的下一版本,但不向后兼容OAuth 1.0即完全廢止了OAuth1.0。 OAuth 2.0關(guān)注客戶端開(kāi)發(fā)者的簡(jiǎn)易性。
那我們?cè)撛趺词褂脜f(xié)議?協(xié)議給我們定義了流程和參數(shù),只要我們雙方都遵循這個(gè)協(xié)議,那就算使用實(shí)現(xiàn)了該協(xié)議。
2.2 OAuth2.0做什么?
目前很多開(kāi)放平臺(tái)如新浪微博開(kāi)放平臺(tái)都在使用提供開(kāi)放API接口供開(kāi)發(fā)者使用,隨之帶來(lái)了第三方應(yīng)用要到開(kāi)放平臺(tái)進(jìn)行授權(quán)的問(wèn)題,OAuth就是干這個(gè)的。
舉個(gè)栗子,咱們?yōu)g覽百度的時(shí)候需要登陸,可以用微信賬號(hào)登錄,那這時(shí)候微信給百度的賬號(hào)信息就可以通過(guò)授權(quán)(OAuth2.0)來(lái)實(shí)現(xiàn)了。


2.3 關(guān)鍵字
這部分大家需要記牢,在后面的流程中多次涉及。
- client:第三方應(yīng)用程序,客戶端(百度)
- HTTP service:HTTP服務(wù)提供商,資源信息提供方(微信)
- Resource Owner:資源所有者,用戶(有微信賬號(hào)的百度使用者)
- User Agent:用戶代理,瀏覽器
- Authorization server:認(rèn)證服務(wù)器,即服務(wù)提供商(微信)專門(mén)用來(lái)處理認(rèn)證的服務(wù)器。
- Resource server:資源服務(wù)器,即服務(wù)提供商(微信)存放用戶生成的資源的服務(wù)器。它與認(rèn)證服務(wù)器,可以是同一臺(tái)服務(wù)器,也可以是不同的服務(wù)器。
2.4 運(yùn)行流程

(A)用戶打開(kāi)客戶端以后,客戶端要求用戶給予授權(quán)。
(B)用戶同意給予客戶端授權(quán)。
(C)客戶端使用上一步獲得的授權(quán),向認(rèn)證服務(wù)器申請(qǐng)令牌。
(D)認(rèn)證服務(wù)器對(duì)客戶端進(jìn)行認(rèn)證以后,確認(rèn)無(wú)誤,同意發(fā)放令牌。
(E)客戶端使用令牌,向資源服務(wù)器申請(qǐng)獲取資源。
(F)資源服務(wù)器確認(rèn)令牌無(wú)誤,同意向客戶端開(kāi)放資源。
以上是OAuth2.0的運(yùn)行流程,其中ABCD如何授權(quán)是咱們關(guān)注的主要流程。
3.客戶端的授權(quán)模式
在上節(jié)我們明白了OAuth2.0的運(yùn)行流程,那咱們這節(jié)主要看一下如何獲取授權(quán)的方法。
客戶端必須得到用戶的授權(quán)(authorization grant),才能獲得令牌(access token)。OAuth 2.0定義了四種授權(quán)方式。
- 授權(quán)碼模式(authorization code)
- 簡(jiǎn)化模式(implicit)
- 密碼模式(resource owner password credentials)
- 客戶端模式(client credentials)
上面的四種方法只有授權(quán)碼模式應(yīng)用廣泛,所以本文只講授權(quán)碼模式,其余三個(gè)會(huì)在下面給大家鏈接,有興趣的可以查看。
3.1 授權(quán)碼模式運(yùn)行流程

以下我們還是采用用微信賬號(hào)登錄百度的栗子講解。(括號(hào)中均為舉例)
(A)用戶訪問(wèn)客戶端,后者將前者導(dǎo)向認(rèn)證服務(wù)器。(當(dāng)我們?cè)诎俣瓤蛻舳它c(diǎn)擊微信登錄,會(huì)在百度客戶端中跳轉(zhuǎn)到微信授權(quán)頁(yè)面,詢問(wèn)用戶是否同意授權(quán)。顯示的授權(quán)URI是微信提供的,其中帶有部分參數(shù),包括授權(quán)成功后的重定向URI,該重定向URI是百度提供的。)
(B)用戶選擇是否給予客戶端授權(quán)。
(C)假設(shè)用戶給予授權(quán),認(rèn)證服務(wù)器將用戶導(dǎo)向客戶端事先指定的"重定向URI"(redirection URI),同時(shí)附上一個(gè)授權(quán)碼。(在A步驟中,我們傳遞了重定向URI,那么微信授權(quán)頁(yè)面就知道授權(quán)成功后,應(yīng)該跳轉(zhuǎn)到該重定向URI,并向百度傳遞授權(quán)碼。該步驟在瀏覽器中可見(jiàn)。)
(D)客戶端收到授權(quán)碼,附上早先的"重定向URI",向認(rèn)證服務(wù)器申請(qǐng)令牌。這一步是在客戶端的后臺(tái)的服務(wù)器上完成的,對(duì)用戶不可見(jiàn)。(百度拿著授權(quán)碼和一些參數(shù),去微信申請(qǐng)?jiān)L問(wèn)令牌。)
(E)認(rèn)證服務(wù)器核對(duì)了授權(quán)碼和重定向URI,確認(rèn)無(wú)誤后,向客戶端發(fā)送訪問(wèn)令牌(access token)和更新令牌(refresh token)。(微信返回給百度訪問(wèn)令牌和更新令牌,百度就能拿著訪問(wèn)令牌去訪問(wèn)微信的信息了。)
3.2 授權(quán)碼模式所需參數(shù)
該步驟對(duì)應(yīng)3.1的步驟
(A)訪問(wèn)授權(quán)頁(yè)面URI
- response_type:表示授權(quán)類型,此處的值固定為"code",必選項(xiàng)
- client_id:表示客戶端的ID,在授權(quán)方的開(kāi)放平臺(tái)申請(qǐng)的appid,是雙方的一個(gè)身份證明,必選項(xiàng)
- redirect_uri:表示重定向URI。當(dāng)用戶同意授權(quán),服務(wù)商回應(yīng)客戶端的uri,可選項(xiàng)建議寫(xiě)。
- scope:表示申請(qǐng)的權(quán)限范圍,可選項(xiàng)
- state:表示客戶端的當(dāng)前狀態(tài),可以指定任意值,認(rèn)證服務(wù)器會(huì)原封不動(dòng)地返回這個(gè)值。
例子:
/authorize ? response_type=code & client_id=s6BhdRkqt3 & state=xyz & redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
(C)服務(wù)商給重定向URI傳回Code
C步驟中,假設(shè)用戶同意授權(quán),服務(wù)商會(huì)訪問(wèn)重定向URI,并包含以下參數(shù):(用戶同意用微信賬號(hào)登錄百度,微信訪問(wèn)百度給的重定向URI,并拼上參數(shù))
- code:表示授權(quán)碼,必選項(xiàng)。該碼的有效期應(yīng)該很短,通常設(shè)為10分鐘,客戶端只能使用該碼一次,否則會(huì)被授權(quán)服務(wù)器拒絕。該碼與客戶端ID和重定向URI,是一一對(duì)應(yīng)關(guān)系。(微信給百度的授權(quán)碼)
- state:如果客戶端的請(qǐng)求中包含這個(gè)參數(shù),認(rèn)證服務(wù)器的回應(yīng)也必須一模一樣包含這個(gè)參數(shù)。
例子:
https://client.example.com/cb ? code=SplxlOBeZQQYbYS6WxSbIA & state=xyz
(D)用Code換Token
在C步驟中拿到Code之后,客戶端(百度)向認(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ù)值保持一致。(這個(gè)參數(shù)會(huì)做一個(gè)驗(yàn)證)
- client_id:表示客戶端ID,在授權(quán)方開(kāi)放平臺(tái)申請(qǐng)的appid,必選項(xiàng)。(百度在微信開(kāi)放平臺(tái)申請(qǐng)的appid)
- client_secret:表示客戶端Secret,在授權(quán)方開(kāi)放平臺(tái)申請(qǐng)的secret,必選項(xiàng)。(百度在微信開(kāi)放平臺(tái)申請(qǐng)的secret)
注意: 該步中的信息一定要保證正確,且只有該步驟可以暴露secret,因?yàn)椴粫?huì)暴露給外網(wǎng)。
(E)返回Token格式
E步驟中,認(rèn)證服務(wù)器發(fā)送的HTTP回復(fù),包含以下參數(shù):
- access_token:表示訪問(wèn)令牌,必選項(xiàng)。
- token_type:表示令牌類型,該值大小寫(xiě)不敏感,必選項(xiàng),可以是bearer類型或mac類型。
- expires_in:表示過(guò)期時(shí)間,單位為秒。如果省略該參數(shù),必須其他方式設(shè)置過(guò)期時(shí)間。
- refresh_token:表示更新令牌,用來(lái)獲取下一次的訪問(wèn)令牌,可選項(xiàng)。
- scope:表示權(quán)限范圍,如果與客戶端申請(qǐng)的范圍一致,此項(xiàng)可省略。
例子:返回值采用JSON格式
{
"access_token":"2YotnFZFEjr1zCsicMWpAA",
"token_type":"example",
"expires_in":3600,
"refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
"example_parameter":"example_value"
}
3.3 appid和secret的存在意義
這兩個(gè)參數(shù)都是從授權(quán)方的開(kāi)放平臺(tái)申請(qǐng)獲取的。
如果 appid 是為了告訴身份認(rèn)證服務(wù)器,『我是 百度』;那么 secret 是為了告訴身份認(rèn)證服務(wù)器,『我真的是 百度』。A步驟中,為了告訴身份認(rèn)證服務(wù)你是哪家第三方服務(wù),你總是需要暴露一些信息(appid)給身份認(rèn)證服務(wù)器的(暴露是指用戶能獲取到,比如會(huì)在地址欄里出現(xiàn))。
獲取 code 的時(shí)候,那能暴露的只能是 appid,但如果數(shù)據(jù)交互可以不暴露給用戶,比如獲取 access token 那一步,D步驟,由第三方服務(wù)器內(nèi)部直接發(fā)起,用戶并不可見(jiàn),那會(huì)帶上 secret。secret 為什么叫 secret,就因?yàn)樗^對(duì)不能暴露給外網(wǎng)。secret其實(shí)就是第三方網(wǎng)站與 OAuth 服務(wù)網(wǎng)站之間的信物。此信物是一定一定不能被第三者知道的。如果知道了一定要第一時(shí)間重新生成。
3.4 Code的存在意義
可能看到這,大家會(huì)問(wèn),Code有必要存在嗎?我為什么不能直接申請(qǐng)token呢?
OAuth 2.0 當(dāng)初設(shè)計(jì)的一個(gè)目標(biāo)之一是,讓不支持 https 的網(wǎng)站也能安全使用。既然提到了 https,必然跟中間人攻擊有關(guān)系。
我們先舉個(gè)栗子:
我們先假設(shè) OAuth 不需要整什么 code,就直接獲取 access token,那么流程就是(還是拿 baidu.com 和微信舉例):
用戶瀏覽器訪問(wèn) baidu.com,baidu.com 服務(wù)器發(fā)現(xiàn)用戶處于未登錄狀態(tài),返回 302,讓瀏覽器跳轉(zhuǎn)到微信 OAuth 服務(wù)獲取 access token,(假設(shè)為 https://wx.qq.com/token?appid=xxx&redirect_uri=http://chrisyue.com&scope=...)
用戶在微信的網(wǎng)頁(yè)上完成了賬號(hào)密碼的輸入并登錄成功(或者已經(jīng)登錄授權(quán)成功),微信服務(wù)器也返回 302,讓瀏覽器跳轉(zhuǎn)到 redirect_uri 指定的地址并且?guī)?access token 參數(shù)。
用戶瀏覽器訪問(wèn)帶 access token 的鏈接,完成整個(gè)登錄。
此流程沒(méi)大毛病,就是最后一步,如果 chrisyue.com 是不支持 https 訪問(wèn)的,那么 access token 就等于是暴露在瀏覽器和 chrisyue.com 服務(wù)器之間的線路中。
當(dāng)然,從另外一方面來(lái)說(shuō),如果第三方網(wǎng)站強(qiáng)行要求必須支持 https,理論上來(lái)說(shuō),code 這一步也是可以省的。
可能也有人會(huì)問(wèn),那多一步獲取 code 有什么用呢?如果 baidu.com 不支持 https,code 不也是會(huì)被暴露嗎?攻擊者拿到 code,類似與之前討論 redirect_uri 是否可以不檢查域名時(shí)所做的一樣,直接用獲取到的 code,訪問(wèn) baidu.com 獲取 access token 的地址來(lái)登錄受害人的賬號(hào)。針對(duì)這個(gè)問(wèn)題,OAuth2.0 協(xié)議其實(shí)對(duì)此是有處理方式的:
- code 只能被使用一次。
- 若是攻擊者比正常用戶先用了 code 也沒(méi)事,因?yàn)槿绻粋€(gè) code 被用了兩次,之前通過(guò)此 code 獲取的 access token 將被撤回,而因?yàn)槠胀ㄓ脩舯緛?lái)就是要訪問(wèn)拿 code 換 access token 的地址,code 是一定會(huì)被用的
也就是說(shuō),攻擊者最多讓正常用戶有點(diǎn)困擾,可能會(huì)出現(xiàn)登錄意外失敗,或者明明看起來(lái)登錄成功但還是獲取不到用戶信息的情況(access token 已經(jīng)失效),但攻擊者依舊拿不到數(shù)據(jù)。
4.資料
4.1 大佬的教學(xué)文章
以下文章沒(méi)有先后,會(huì)讓大家更深入了解OAuth2.0,如果大家不喜歡我這篇,以下幾篇足夠用了:
理解OAuth 2.0
OAuth2集成——《跟我學(xué)Shiro》
關(guān)于 OAuth2.0 安全性你應(yīng)該要知道的一些事
4.2 微博開(kāi)放平臺(tái)API
現(xiàn)在微博,微信,支付寶等用的都是OAuth2.0協(xié)議,給大家拿微博的API看下,可以確認(rèn)自己學(xué)的不是假的。(/ω\)
5.總結(jié)
本篇文章我們圍繞著:
- OAuth2.0是什么?做什么?
- 關(guān)鍵字與運(yùn)行流程
- 四種授權(quán)模式(授權(quán)碼模式)
- Code的作用
以上四個(gè)方面對(duì)OAuth2.0進(jìn)行了學(xué)習(xí),希望大家讀完這篇文章,會(huì)對(duì)OAuth2.0有一個(gè)更深入的了解。如果我的文章能給大家?guī)?lái)一點(diǎn)點(diǎn)的福利,那在下就足夠開(kāi)心了。
下次再見(jiàn)!
