1 OAuth簡述
OAuth 2.0 是一個授權協定,它允許軟體應用代表(而不是充當)資源擁有者去通路資源擁有者的資源。應用向資源擁有者請求授權,然後取得令牌(token),并用它來通路資源,并且資源擁有者不用向應用提供使用者名和密碼等敏感資料。
2 OAuth角色
OAuth整個授權過程中定義了4種角色:
用戶端(Cilent):代表資源擁有者通路受保護資源的軟體,它使用OAuth 來擷取通路權限;
資源擁有者(Resource Owner):是有權将通路權限授權給用戶端的主體,在大多數情況下,資源擁有者是一個人,他使用用戶端軟體通路受他控制的資源;
資源伺服器(Resource Server):資源伺服器能夠通過HTTP 伺服器進行通路,在通路時需要OAuth 通路令牌。受保護資源需要驗證收到的令牌,并決定是否響應以及如何響應請求;
授權伺服器(Authorization Server):一個HTTP 伺服器,它在OAuth 系統中充當中央元件。授權伺服器對資源擁有者和用戶端進行身份認證,讓資源擁有者向用戶端授權、為用戶端頒發令牌。某些授權伺服器還會提供額外的功能,例如令牌内省、記憶授權決策;
假設你使用了一個照片雲存儲服務和一個雲列印服務,并且想使用雲列印服務來列印存放在雲存儲服務上的照片。很幸運,這兩個服務能夠使用API 來通信。這很好,但兩個服務由不同的公司提供,這意味着你在雲存儲服務上的賬戶和在雲列印服務上的賬戶沒有關聯。使用OAuth 可以解決這個問題:授權雲列印服務通路照片,但并不需要将存儲服務上的賬戶密碼交給它。 在這上面這一段中: 用戶端 :雲列印服務 資源擁有者:你 資源伺服器,授權伺服器:照片雲存儲服務
3 OAuth授權許可類型
資源擁有者通過在授權伺服器完成登入,擷取授權碼的形式,用戶端通過Ajax請求最終兌換令牌,該種形式使得令牌對外是不可見的,一定程度上保證了令牌的安全,通常用于Web端的OAuth2.0 開發。
具體流程步驟為:
(1)資源擁有者在授權伺服器完成登入,并完成授權,資源伺服器重定向至用戶端,并附帶一個臨時授權碼,該授權碼是短時效性的;
(2)用戶端擷取到臨時授權碼後,通過臨時授權碼以及其他應用資訊,向資源伺服器接口請求,以此來換取令牌(後端接口請求)。

有些 Web 應用是純前端應用,沒有後端。此時就沒有使用授權碼形式的必要了,而是直接傳回令牌,省略了授權碼再去兌換令牌的步驟(該步驟本是在後端進行,目的是對前端隐藏令牌内容)。隐式許可流程不可用于擷取重新整理令牌。因為浏覽器内的應用具有短暫運作的特點,隻會在被加載到浏覽器的期間保持會話。
後端系統之間需要直接通信,且本身并不代表某個特定使用者,沒有使用者對用戶端授權。用戶端直接向授權伺服器進行身份認證,而授權伺服器給用戶端頒發通路令牌。需要提供用戶端id以及key(在對接前,手動申請)
其大緻的請求如下:
在斷言許可類型下,用戶端會得到一條結構化的且被加密保護的資訊,叫作斷言,使用斷言向授權伺服器換取令牌。這種許可類型隻使用後端信道,與用戶端憑據許可類型很相似,沒有明确的資源擁有者參與。與用戶端憑據流程不同的是,由此頒發的令牌所關聯的權限取決于所出示的斷言,而不僅僅取決于用戶端本身。由于斷言一般來自于用戶端之外的第三方,是以用戶端可以不知道斷言本身的含義。
目前标準的斷言格式:
安全斷言标記語言:SAML
JSON WEB TOKEN:JWT
如果資源擁有者在授權伺服器上有純文字的使用者名和密碼,那麼用戶端可以向使用者索取使用者的憑據,然後用這個憑據換取令牌,也叫作密碼流程。
資源擁有者與之直接互動的是用戶端,而不是授權伺服器。這種許可類型隻使用令牌端點,并且隻通過後端信道通信。
4 OAuth 安全相關
授權碼許可和隐式許可類型中都可以使用的state 參數,這個參數是一個随機數,作為接口請求參數。用戶端使用state參數來維持請求與回調之間狀态的不透明值。授權伺服器在将使用者代理重定向回用戶端時包含該值。應該使用這個參數,它可以防止CSRF(cross-site request forgery,跨站請求僞造)。後續有實際例子。
授權碼使用一次之後将其銷毀。
授權伺服器應該采用精确比對的重定向URI 校驗算法,這是唯一安全的方法。
完全按照OAuth 核心規範來實作授權伺服器可能會導緻它成為一個開放重定向器。如果這個重定向器能受到妥善的監控,則情況還好,但稍有不慎則會面臨風險。
留意在進行錯誤提示的過程中,資訊有可能通過URI 片段或者Referrer 頭部遭洩露。
授權碼可以避免将令牌直接暴露給其他人,但是授權碼仍可能白劫持。授權碼本身是沒有用的,特别是用戶端擁有用于自身身份認證的密鑰的情況下。然而,原生應用在用戶端密鑰方面存在特殊問題(APP可能需要把密鑰寫死在源代碼中導緻洩露);
解決辦法:PKCE(Proof Key for Code Exchange)
(1)用戶端建立并記錄名為code_verifier的秘密資訊
(2)用戶端根據code_verifier計算code_challenge(例如:密碼散列MAC)
(3)用戶端請求授權伺服器,并附帶code_challenge以及code_challenge_method(計算方法,可選);
(4)授權伺服器正常響應,并記錄code_challenge和code_challenge_method(與授權碼關聯);
(5)用戶端接收到授權碼後,攜帶之前生成的code_verifier,執行令牌申請請求;
(6)授權伺服器計算code_challenge,檢測是否一緻。
5 OAuth 令牌
OAuth 系統中的用戶端無須了解令牌本身的任何資訊。用戶端需要知道的就是如何從授權伺服器擷取令牌以及如何在資源伺服器上使用令牌。但是,授權伺服器和資源伺服器需要了解令牌的内容。授權伺服器要知道如何生成令牌來頒發給用戶端,資源伺服器要知道如何識别并驗證用戶端發送過來的令牌。
OAuth 令牌可以具有有效期,可以支援撤回,也可以永久有效,或者根據情況将這些特性組合;
令牌可以代表特定的使用者或者系統中所有的使用者,也可以不代表任何使用者;
令牌可以具有内部結構,可以是随機的無意義字元串,也可以被加密保護,甚至可以将這幾項結合起來。
授權伺服器生成令牌之後,會将令牌值存儲在磁盤上的共享資料庫中(非必須)。當受保護資源從用戶端收到令牌之後,它會在同一個資料庫中查找令牌值,以确定令牌有效。這種令牌不攜帶任何資訊,隻是充當資料庫查詢的檢索值。
可參考内容:JSON Web Tokens - jwt.io
優勢:不向共享資料庫查詢,将所有必要的資訊放在令牌内部,授權伺服器可以通過令牌本身間接地與受保護資源溝通,而不需要調用任何網絡API。
劣勢:頒發的令牌無法撤回。
JWT 的核心是将一個JSON 對象封裝為一種用于網絡傳輸的格式。整體機構通過句點分割,每個部分是由Base64URL編碼的JSON對象,通過三個部分組成:
頭部(head):聲明簽名算法,以及負載類型
負載(payload):使用者資料
簽名(signature):對前兩部分的簽名
這裡分享兩種算法:
(1)HAMC算法:使用對稱密鑰,授權伺服器和資源伺服器都能夠生成令牌,因為它們都擁有建立令牌所需的密鑰。
(2)RSA算法:使用公鑰加密的話,授權伺服器擁有公鑰和私鑰,可用于生成令牌,而受保護資源則隻能通路授權伺服器的公鑰,用于驗證令牌。與HMAC相比,資源伺服器就無法自己生成有效的令牌。
令牌請求資源伺服器時,再對其進行驗簽,驗簽成功再解析payload部分。隻有簽名有效才能繼續解析JWT 并檢查其内容的一緻性。如果所有檢查都通過,就可以将它交給應用使用。
将令牌資訊打包放入令牌本身也有其不足之處。為了包含所有必要的聲明以及保護這些聲明所需的密碼結構,令牌尺寸會變得非常大。而且,如果受保護資源完全依賴令牌本身所包含的資訊,則一旦将有效的令牌生成并釋出,想要撤回會非常困難。
令牌内省:授權伺服器向用戶端頒發令牌,用戶端向受保護資源出示令牌,受保護資源則向授權伺服器查詢令牌狀态(内省請求:資源伺服器發送給授權伺服器内省端點的表單形式的HTTP 請求,詢問該令牌是否有效)
受保護資源在請求過程中需要向授權伺服器進行身份認證(提供client_id以及client_secret),以便授權伺服器知道是誰在詢問,并可能根據詢問者的身份傳回不同的響應。内省協定隻是要求受保護資源進行身份認證,并未規定如何認證。
内省協定規範還在JWT 的基礎上增加了幾個聲明定義,其中最重要的是active 聲明。此聲明告訴受保護資源目前令牌在授權伺服器上是否有效,且是唯一必須傳回的聲明。 使用令牌内省會導緻OAuth 系統内的網絡流量增加。為了解決這個問題,允許受保護資源緩存給定令牌的内省請求結果。建議設定短于令牌生命周期的緩存有效期,以便降低令牌被撤回 但緩存還有效的可能性。
OAuth 令牌通常遵循一個可預測的生命周期。令牌由授權伺服器建立,由用戶端使用,并由受保護資源驗證。它們可能會自行失效,也可能被資源擁有者(或者管理者)從授權伺服器上撤回。對于JWT等格式的令牌無效,隻能過期但無法失效。
OAuth 令牌撤回是一個簡單的協定,它讓用戶端可以很簡潔地告訴授權伺服器将本來有效的令牌撤回。用戶端需要向一個專門的撤回端點發送附帶身份認證的HTTP POST 請求,并将要撤回的令牌作為表單參數放入請求主體。
用戶端身份認證時使用的憑據與在令牌端點上使用的憑據相同(判斷是否時該用戶端申請的令牌)。授權伺服器會查詢令牌值,如果找到令牌,會将它從存儲令牌的地方删除,并傳回響應告知用戶端删除成功。如果授權伺服器未找到令牌,或者不允許出示令牌的用戶端撤回該令牌,授權伺服器還是會傳回操作成功,目的是不向用戶端透露本不屬于它的令牌資訊。
重新整理令牌由授權伺服器頒發給用戶端。在OAuth 中,通路令牌随時可能失效。令牌有可能被使用者撤銷,也可能過期,或者其他系統導緻令牌失效。通路令牌失效後,用戶端在使用時會收到錯誤響應。當然,用戶端可以再次向資源擁有者請求權限,也可以使用重新整理令牌向授權伺服器請求新的通路令牌。重新整理令牌還可以讓用戶端縮小它的權限範圍。如果用戶端被授予A、B、C 三個權限範圍,但是它知道某特定請求隻需要A 權限範圍,則它可以使用重新整理令牌重新擷取一個僅包含A 權限範圍的通路令牌
6 可參考的線上OAuth接口文檔
QQ互聯:使用Authorization_Code擷取Access_Token — QQ互聯WIKI WeChat OAuth:準備工作|微信開放文檔 支付寶 OAuth:使用者授權 Google OAuth:Using OAuth 2.0 for Web Server Applications | Google Identity Platform Github OAuth:AuthorizingOAuth Apps - GitHub Docs
7 OAuth 線上個人OAuth申請
目前可知的,支付寶以及Github均支援以給個人身份申請接入,本次示例以Github為例。
支付寶申請位址:申請網頁應用
Github申請位址:申請OAuth應用
填寫完上述内容後,送出會擷取到app_id以及app_secret,這樣我們就可以開始對接了,具體内容可以參考第6部分的文檔進行
8 Postman OAuth配置
因為第7部分我們已經申請到了github的OAuth2應用,則我們可以直接通過Postman進行快捷的接口測試,以github擷取使用者基本資訊接口為例。
該接口請求為:GET https://api.github.com/user
點開Get New Access Token進行配置,相關配置可以參考第六部分提供的文檔
AuthUrl:https://github.com/login/oauth/authorize
AccessTokenUrl:https://github.com/login/oauth/access_token
這樣我們就可以擷取到一個通路令牌調用接口了。