web:http://oauth.net/2/
rfc:http://tools.ietf.org/html/rfc6749
doc:http://oauth.net/documentation/
code:http://oauth.net/code/
OAuth2.0授權類型
OAuth2.0協議定義了用於獲得授權的四種主要授權類型。
Authorization code
標准的Server授權模式,非常適合Server端的Web應用。一旦資源的擁有者授權訪問他們的數據之后,他們將會被重定向到Web應用並在URL的查詢參數中附帶一個授權碼(code)。在客戶端里,該code用於請求訪問令牌(access_token)。並且該令牌交換的過程是兩個服務端之前完成的,防止其他人甚至是資源擁有者本人得到該令牌。另外,在該授權模式下可以通過refresh_token來刷新令牌以延長訪問授權時間。
Implicit Grant
該模式是所有授權模式中最簡單的一種,並為運行於瀏覽器中的腳本應用做了優化。當用戶訪問該應用時,服務端會立即生成一個新的訪問令牌(access_token)並通過URL的#hash段傳回客戶端。這時,客戶端就可以利用JavaScript等將其取出然后請求API接口。該模式不需要授權碼(code),當然也不會提供refresh token以獲得長期訪問的入口。
Resource Owner Password Credentials
這種模式要求用戶提供用戶名和密碼來交換訪問令牌(access_token)。該模式僅用於非常值得信任的用戶,例如API提供者本人所寫的移動應用。雖然用戶也要求提供密碼,但並不需要存儲在設備上。因為初始驗證之后,只需將OAuth的令牌記錄下來即可。如果用戶希望取消授權,因為其真實密碼並沒有被記錄,因此無需修改密碼就可以立即取消授權。token本身也只是得到有限的授權,因此相比最傳統的username/password授權,該模式依然更為安全。
Client Credentials
一種基於APP的密鑰直接進行授權,因此APP的權限非常大。它適合像數據庫或存儲服務器這種對API的訪問需求
之前做微信公眾平台,基本流程參照文檔,也沒有太注意OAuth太多細節,最近在與攜程會員對接,陸陸續續折騰好幾天吧,就花了一點時間研究了下OAuth協議,感覺OAuth大行其道路,另外OAuth與OpenID是比較容易混淆的兩個東西,OpenID設計的目的趨向與身份校驗,OAuth設計的目的是對用戶的資源授權。現階段一般互聯網公司應該都支持OAuth2協議,如新浪,阿里,騰訊等,特點是都擁有大量的用戶群,來等待其他開發者與第三方應用的接入,如現在比較火爆的微信公眾平台。
什么是OAuth2.0
OAuth2.0是一個開放協議,允許用戶讓第三方應用以安全且標准的方式獲取該用戶在某一網站、移動或桌面應用上存儲的私密的資源(如用戶個人信息、照片、視頻、聯系人列表),而無需將用戶名和密碼提供給第三方應用。
OAuth 2.0是OAuth協議的下一版本,但不向后兼容OAuth 1.0。 OAuth 2.0關注客戶端開發者的簡易性,同時為Web應用,桌面應用和手機,和起居室設備提供專門的認證流程。
OAuth 允許用戶提供一個令牌,而不是用戶名和密碼來訪問他們存放在特定服務提供者的數據。每一個令牌授權一個特定的網站(例如,視頻編輯網站)在特定的時段(例如,接下來的2小時內)內訪問特定的資源(例如僅僅是某一相冊中的視頻)。這樣,OAuth允許用戶授權第三方網站訪問他們存儲在另外的服務提供者上的信息,而不需要分享他們的訪問許可或他們數據的所有內容。
上面是比較官方的描述,簡單些就是OAuth2是由OAuth1.0發展而來,兩者不向后兼容,支持多平台設備。可以使用自己的賬戶與密碼授權來讓第三方應用在特定的時間內獲得用戶的資源信息。以新浪圍脖為例,比如我習慣用微博登錄一些社交網站,這樣發現有意思的東西,可以快速分享到微博上,嗯,我經常這么干。按照流程一般先需要用戶到新浪上去授權,登錄成功后,在知乎上就可以獲得微博上的頭像與用戶基本信息等。
OAuth2授權流程
在講述OAuth2.0之前先得了解基本授權的基本術語:
資源擁有者(resource owner):能授權訪問受保護資源的一個實體,如新浪微博用戶 irving;
資源服務器(resource server):存儲受保護資源,客戶端通過access token請求資源,資源服務器響應受保護資源給客戶端;存儲着用戶irving的微博等信息。
授權服務器(authorization server):成功驗證資源擁有者並獲取授權之后,授權服務器頒發授權令牌(Access Token)給客戶端。
客戶端(client):如新浪微博第三方應用,也可以是它自己的官方應用;其本身不存儲資源,而是資源擁有者授權通過后,使用它的授權(授權令牌)訪問受保護資源,然后客戶端把相應的數據展示出來。“客戶端”術語不代表任何特定實現(如應用運行在一台服務器、桌面、手機或其他設備)。
OAuth2授權模式
客戶端必須得到用戶的授權(authorization grant),才能獲得令牌(access token)。OAuth 2.0定義了四種授權方式。
- 授權碼模式(authorization code)
- 簡化模式(implicit)
- 密碼模式(resource owner password credentials)
- 客戶端模式(client credentials)
因為一般授權碼的使用的比較多,客戶端可能會使用簡化模式或客戶端模式,我這里只簡述授權碼模式,授權碼流程
+----------+
| 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)
(A)用戶授權訪問認證服務器
(B)用戶選擇是否給予客戶端授權
(C)如果給予授權,認證服務器(authorization service)將重定向(http狀態碼302)到A流程中給予的URL(redirect_uri),同時附上一個授權碼(code)
(D)客戶端收到授權碼(code),向認證服務器(authorization service)申請令牌(發送post請求)。這個過程是在客戶端的后台請求上完成的,對用戶不可見
(E)認證服務器檢查授權碼(code)和重定向URI,確認無誤后,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)以及失效時間(expires_in)
如新浪的授權流程圖:
1、客戶端從資源擁有者給予授權
比如我現在使用新浪微博的賬戶授權登錄知乎。
GET /oauth/auth/sina?next=/oauth/account_callback HTTP/1.1
Host: www.zhihu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.zhihu.com/請求包含以下參數:
- response_type:表示授權類型,必選項,此處的值固定為"code"
- client_id:表示客戶端的ID,必選項
- redirect_uri:表示重定向URI,可選項
- scope:表示申請的權限范圍如 user,order,可選項
- state:表示客戶端的當前狀態,可以指定任意值,認證服務器會原封不動地返回這個值。
一般開發一個基於新浪微博app的第三方應用,需要申請appkey和appsecret分別對應OAuth2.0中的client_id與client_secret,可能一些服務端會擴展一些其他的名稱,騰訊就是這個干的。
2、用戶同意給予客戶端授權
分兩種情況,如果是沒有微博登錄成功的會話,就讓用戶登錄,如上圖如果之前我已經有了登錄成功的會話了,這個時候就讓用戶確認同意授權(知乎好流氓,直接略了這一步,但是流程還是一樣的),我用其他網站的聯合登錄截了個圖如下。
CONNECT api.weibo.com:443 HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Connection: keep-alive
Connection: keep-alive
Host: api.weibo.com:443
后面我使用Fiddler抓了下包流程還是一樣的,還是一樣會302跳轉,另外發現還使用了socketio,貌似服務端有基於NodeJS的服務。
GET /socket.io/1/?t=1417252232035 HTTP/1.1
Host: comet.zhihu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.zhihu.com/
Origin: http://www.zhihu.com
Connection: keep-alive
返回:
HTTP/1.1 200 OK
Server: zhihu_nginx
Date: Sat, 29 Nov 2014 09:11:19 GMT
Content-Type: text/plain
Connection: keep-alive
Vary: Accept-Encoding
Access-Control-Allow-Origin: http://www.zhihu.com
Access-Control-Allow-Credentials: true
Content-Length: 36rfmX2x_OC9aKyzZAGh3c:60:60:websocket
GET /oauth/auth/request_sina_token?next=%2Foauth%2Faccount_callback&state=78db9a8b58b2dab6970d4520907ad629&code=879c3a9aabdf5dfe69b92092204fe480 HTTP/1.1
Host: www.zhihu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.zhihu.com/
Connection: keep-alive授權成功返回如下參數:
- code:表示授權碼,必選項。通常授權碼只能使用該碼一次,並且會設置有效時間。
- state:如果客戶端的請求中包含這個參數,認證服務器的回應也必須一模一樣包含這個參數。
3、客戶端使用授權碼向認證服務器申請令牌。
這個過程發生在客戶端后台,用戶不可見,這也是授權碼模式的特性,更安全。大致接口地址類似(服務端會驗證client_id、client_secret、auth code的正確性或更新令牌 refresh_token):
http://localhost:8080/oauth2/access_token?client_id={AppKey}&client_secret={AppSecret}&grant_type=authorization_code&redirect_uri={YourSiteUrl}&code={code}
向認證服務器申請令牌包含以下參數:
- grant_type:表示使用的授權模式,必選項,此處的值固定為"authorization_code"。
- code:表示上一步獲得的授權碼,必選項。
- redirect_uri:表示重定向URI,必選項,且必須與A步驟中的該參數值保持一致。
- client_id:表示客戶端ID,必選項
- client_secret:表示客戶端密鑰,必選項。
4、如果授權碼驗證成功,則下生成一個令牌。
這個過程發生在服務端后台,服務端會驗證client_id、client_secret、auth code的合法性,成功則生成一個令牌。
認證服務器返回,包含以下參數:
- access_token:表示訪問令牌,必選項。
- token_type:表示令牌類型,該值大小寫不敏感,必選項,可以是bearer類型或mac類型。
- expires_in:表示過期時間,單位為秒。如果省略該參數,必須其他方式設置過期時間。
- refresh_token:表示更新令牌,用來獲取下一次的訪問令牌,可選項。
- scope:表示權限范圍,如果與客戶端申請的范圍一致,此項可省略。
5、客戶端使用訪問令牌向資源服務器請求受保護資源。
客戶端實現,請求用戶保護的資源。
知乎成功后返回:
GET /oauth/account_callback?callbacktype=sina HTTP/1.1
Host: www.zhihu.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:33.0) Gecko/20100101 Firefox/33.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: http://www.zhihu.com/
Connection: keep-alive
6、資源服務器驗證訪問令牌的有效性則開放保護的資源。
服務端實現,驗證令牌合法性后則開放用戶授權的保護資源,很多時候,用戶的資源是與權限對應的。令牌可能只包含了用戶特定的授權憑據,准確的說,令牌對應用戶授權時所賦予的一系列權限的集合。所以在這一步,除了校驗令牌的合法性之外,服務端還需對該令牌是否擁有足夠的權限執行被保護操作進行驗證。
一些問題
簡單例舉我想到的一些問題吧
1.請求授權碼過程服務端最好啟用授權域(scope)需要對授權的令牌的有效權限進行驗證,以免泄露過多用戶數據 ,啟用state,用於保持請求和回調的狀態,授權請求后原樣帶回給第三方。該參數可用於防止csrf攻擊(跨站請求偽造攻擊),建議第三方帶上該參數,可設置為簡單的隨機數加session進行校驗。
2.獲得授權碼重定向的地址,為了防止有攻擊者偽造重定向地址騙取用戶授權,服務提供方應對授權時的重定向地址進行驗證或判斷是不是同一個域,所以申請應用的時候應該有appkey,appsecret,redirect_uri基礎配置參數。
3.確保授權碼一次性與有效時間,令牌有效時間驗證,改用HTTPS來加密,確保通信內容不被第三方竊取。
Refer:
Docs » OAuth 2.0
http://oauthlib.readthedocs.org/en/latest/oauth2/oauth2.html
理解OAuth 2.0
http://www.ruanyifeng.com/blog/2014/05/oauth_2_0.html
OAuth2授權原理
http://www.cnblogs.com/neutra/archive/2012/07/26/2609300.html
OAuth 2.0模式
http://www.dannysite.com/blog/?tag=204
OAuth 2.0安全案例回顧
http://drops.wooyun.org/papers/598