假設有一個「雲筆記」產品,並提供了「雲筆記服務」和「雲相冊服務」,此時用戶須要在不一樣的設備(PC、Android、iPhone、TV、Watch)上去訪問這些「資源」(筆記,圖片)html
那麼用戶如何才能訪問屬於本身的那部分資源呢?此時傳統的作法就是提供本身的帳號和密碼給咱們的「雲筆記」,登陸成功後就能夠獲取資源了。但這樣的作法會有如下幾個問題:git
爲了解決相似以上問題oAuth2應用而生github
OAuth(開放受權)是一個開放標準,容許用戶受權第三方移動應用訪問他們存儲在另外的服務提供者上的信息,而不須要將用戶名和密碼提供給第三方移動應用或分享他們數據的全部內容,OAuth2.0是OAuth協議的延續版本,但不向後兼容OAuth 1.0即徹底廢止了OAuth1.0。json
OAuth 2.0 的標準是 RFC 6749 文件。該文件先解釋了 OAuth是引入了一個受權層,用來分離兩種不一樣的角色:客戶端和資源全部者。......資源全部者贊成之後,資源服務器能夠向客戶端頒發令牌。客戶端經過令牌,去請求數據。OAuth 的核心就是向第三方應用頒發令牌。後端
(A)用戶打開客戶端之後,客戶端要求用戶給予受權。api
(B)用戶贊成給予客戶端受權。瀏覽器
(C)客戶端使用上一步得到的受權,向認證服務器申請令牌。安全
(D)認證服務器對客戶端進行認證之後,確認無誤,贊成發放令牌。服務器
(E)客戶端使用令牌,向資源服務器申請獲取資源。微信
(F)資源服務器確認令牌無誤,贊成向客戶端開放資源。
(A)用戶訪問客戶端,後者將前者導向認證服務器。
(B)用戶選擇是否給予客戶端受權。
(C)假設用戶給予受權,認證服務器將用戶導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個受權碼。
(D)客戶端收到受權碼,附上早先的"重定向URI",向認證服務器申請令牌。這一步是在客戶端的後臺的服務器上完成的,對用戶不可見。
(E)認證服務器覈對了受權碼和重定向URI,確認無誤後,向客戶端發送訪問令牌(access token)和更新令牌(refresh token)。
(A)客戶端將用戶導向認證服務器。
(B)用戶決定是否給於客戶端受權。
(C)假設用戶給予受權,認證服務器將用戶導向客戶端指定的"重定向URI",並在URI的Hash部分包含了訪問令牌。
(D)瀏覽器向資源服務器發出請求,其中不包括上一步收到的Hash值。
(E)資源服務器返回一個網頁,其中包含的代碼能夠獲取Hash值中的令牌。
(F)瀏覽器執行上一步得到的腳本,提取出令牌。
(G)瀏覽器將令牌發給客戶端。
(A)用戶向客戶端提供用戶名和密碼。
(B)客戶端將用戶名和密碼發給認證服務器,向後者請求令牌。
(C)認證服務器確認無誤後,向客戶端提供訪問令牌。
(A)客戶端向認證服務器進行身份認證,並要求一個訪問令牌。
(B)認證服務器確認無誤後,向客戶端提供訪問令牌。
受權碼模式(authorization code)是功能最完整、流程最嚴密的受權模式。它的特色就是經過客戶端的後臺服務器,與"服務提供商"的認證服務器進行互動。
(A) 用戶訪問客戶端,後者將前者導向認證服務器;客戶端申請認證的URI,包含如下參數:
response_type:表示受權類型,必選項,此處的值固定爲"code"
client_id:表示客戶端的ID,必選項
redirect_uri:表示重定向URI,可選項
scope:表示申請的權限範圍,可選項
state:表示客戶端的當前狀態,任意字符串,認證服務器會原封不動地返回這個值,也是用來防止跨站點請求僞造攻擊,必選項
eg: GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
(B) 用戶選擇是否給予客戶端受權。
(C) 假設用戶給予受權,認證服務器將用戶導向客戶端事先指定的"重定向URI"(redirection URI),同時附上一個受權碼;
服務器迴應客戶端的URI,包含如下參數:
code:表示受權碼,必選項。該碼的有效期應該很短,一般設爲10分鐘,客戶端只能使用該碼一次,不然會被受權服務器拒絕。該碼與客戶端ID和重定向URI,是一一對應關係。
state:若是客戶端的請求中包含這個參數,認證服務器的迴應也必須如出一轍包含這個參數
eg: Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
(D) 客戶端收到受權碼,附上早先的"重定向URI",向認證服務器申請令牌。這一步是在客戶端的後臺的服務器上完成的,對用戶不可見;客戶端向認證服務器申請令牌的HTTP請求,包含如下參數:
grant_type:表示使用的受權模式,必選項,此處的值固定爲"authorization_code"。
code:表示上一步得到的受權碼,必選項。
redirect_uri:表示重定向URI 應用程序中的URL,用於在受權後發送用戶,必選項,且必須與A步驟中的該參數值保持一致。
client_id:表示應用程序的客戶端ID,必選項
client_secrect:表示應用程序的客戶機密也便是訪問的資源權限,必選項
eg: POST /token HTTP/1.1 Host: server.example.com Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW Content-Type: application/x-www-form-urlencoded grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb
(E) 認證服務器覈對了受權碼和重定向URI,確認無誤後,向客戶端發送訪問令牌(access token)和更新令牌(refresh token);認證服務器發送的HTTP回覆,包含如下參數:
access_token:表示訪問令牌,必選項。
token_type:表示令牌類型,該值大小寫不敏感,必選項,能夠是bearer類型或mac類型。
expires_in:表示過時時間,單位爲秒。若是省略該參數,必須其餘方式設置過時時間。
refresh_token:表示更新令牌,用來獲取下一次的訪問令牌,可選項。
scope:表示權限範圍,若是與客戶端申請的範圍一致,此項可省略。
eg: 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" }
令牌的有效期到了,若是讓用戶從新走一遍上面的流程,再申請一個新的令牌,極可能體驗很差,並且也沒有必要。OAuth 2.0 容許用戶自動更新令牌。
具體方法是,資源服務器頒發令牌的時候,一次性頒發兩個令牌,一個用於獲取數據,另外一個用於獲取新的令牌(refresh token 字段)。令牌到期前,用戶使用 refresh token 發一個請求,去更新令牌。
https://b.com/oauth/token? grant_type=refresh_token& client_id=CLIENT_ID& client_secret=CLIENT_SECRET& refresh_token=REFRESH_TOKEN
URL中,grant_
type參數爲refresh_
token表示要求更新令牌,client_
id參數和client_
secret參數用於確認身份,refresh_
token參數就是用於更新令牌的令牌。資源服務器驗證經過之後,就會頒發新的令牌。
密碼模式(resource owner password credentials)
受權碼模式(authorization code)
簡化模式(implicit)
客戶端模式(client credentials)
refresh token的初衷主要是爲了用戶體驗不想用戶重複輸入帳號密碼來換取新token,於是設計了refresh token用於換取新token
這種模式因爲沒有用戶參與,並且也不須要用戶帳號密碼,僅僅根據本身的id和密鑰就能夠換取新token,於是不必refresh token
問:爲何 OAuth 2.0 要求申請 client_id
的時候必須輸入一個域名,而且要求 redirect_uri
必須是此域名下的地址?
答:若是別人知道了咱們的 client_id
,而後在第二部生成受權鏈接的時候,把 redirect_uri
替換成本身與域名下的,就會能獲取 code
並存放在本身服務器上
若是 第三反方服務
不支持https,就會有被劫持的風險。若是沒有 code
,而是直接返回 access_token
在 redirect_uri
中,中間人就能夠直接使用 access_token
若是 code
被中間人獲取,在沒有 app_secret
的狀況下,就能夠直接用 code
換取 access_token
。由於在整個獲取 code
的過程當中都是暴露在外的
state
state
參數若是利用起來,看成 CSRF Token
,就能避免此事的發生:
`state` 或者說 `CSRF Token` 這種跟設備綁定的隨機字符串,只要稍微複雜一點,攻擊者根本就不可能猜得出來,而設置一個讓攻擊者猜不到的,跟設備或者說瀏覽器綁定的 state (CSRF token) 值,就是解決 CSRF 攻擊的關鍵。
關於Auth2的的實現機制,就是不一樣的客戶端須要向資源服務器經過請求令牌(access_
token)來獲取不一樣的資源,服務器須要去驗證訪問的客戶端是不是在該資源服務器註冊過的,以及該客戶端被分配到的資源服務器的可訪問的權限是什麼,因此須要用戶首先被客戶端重定向到認證服務器來驗證它的身份,驗證經過,返回一個用來獲取token的只能使用一次的一個code碼做爲請求token的參數,客戶端請求token的時候還必須傳遞一個須要獲取資源權限的必選參數client_
secrect,以及參數client_
id(客戶端的身份),驗證經過,則客戶端獲取到資源並展現給用戶。
參考資料:
https://www.jianshu.com/p/a7d...
http://www.ruanyifeng.com/blo...
http://www.ruanyifeng.com/blo...
http://www.ruanyifeng.com/blo...
https://developer.github.com/...
https://www.jianshu.com/p/7b1...