一、開篇
《Spring OAuth2 開發指南》是系列文章,詳細介紹基於 Spring 生態(包括 Spring Cloud) OAuth2 的實戰開發。本系列將由五篇文章組成:
- (一)體系架構和開發概覽:是系列文章的開篇,主要對 OAuth2 的體系架構和主要流程進行梳理剖析,並對當前 Spring OAuth2 開發做一個概括性、全局性介紹;
- (二)OAuth2 密碼模式開發實例
- (三)OAuth2 客户端模式開發實例
- (四)OAuth2 授權碼模式開發實例
- (五)OAuth2 微服務場景實例開發:以密碼模式為例,介紹在微服務場景下使用 OAuth2 以及整合 JWT 的關鍵技術方法,主要圍繞客户端、微服務網關、認證授權服務進行,不涉及微服務的其他模塊等。
本系列第一篇主要聚焦 OAuth2 體系的概念介紹,其餘各篇偏向於實戰,主要實現 OAuth2 的三種授權模式:密碼模式、客户端模式和授權碼模式,包括展示授權服務器、資源服務器、客户端等幾種角色的交互,以及 JWT 的整合。並且每個實例都提供兩個代碼版本:一個是基於舊的 Spring Security OAuth2 組件;一個是基於新的 Spring Authorization Server 組件。
需要注意的是 password 模式由於 OAuth2.1 不推薦使用所以只能提供舊的組件代碼版本,具體請參見 https://datatracker.ietf.org/...
如果對認證和授權平台的相關理論感興趣,可以移步本人早前的兩篇文章:
- 平台級 SaaS 架構的基礎:統一身份管理系統, https://www.jianshu.com/p/990...
- 微服務架構下的統一身份認證和授權, https://www.jianshu.com/p/257...
二、OAuth2 體系結構
OAuth 授權體系設計之初主要是為了解決第三方應用登錄和授權的問題,但由於其嚴格規範的流程定義,廣泛的授權通用性,且與具體技術平台無關等諸多優點,逐漸發展成為認證和授權領域的主流技術規範。但其實 OAuth2 規範歸納起來並不複雜,就四種主要的授權模式和五種角色。
四種授權模式,分別是:
- 授權碼模式(authorization code)
- 簡化模式(implicit)
- 密碼模式(resource owner password credentials)
- 客户端模式(client credentials)
五種主要的角色,分別是:
- 用户代理/瀏覽器 User Agent
- 客户端 Client
- 資源所有者 Resource Owner
- 資源服務器 Resource Server (受保護資源)
- 授權服務器 Authorization Server
建議熟練掌握以上概念,理清其中的關係,有助於對整個體系有一個全局性的瞭解。倘若一上來就擼代碼,那造出來的系統必然不健康。須知身份管理體系是任何平台的重要基礎設施,一旦建造的不牢固,必然會種下隱患,好似大廈地基埋下的一顆延遲炸彈。
回到主題,四種模式都有其特定的使用場景,但是在落地過程中,也可以根據實際情況自行取捨。為了方便接下來的介紹,我們假設兩套演示案例:基於圖像的物品分類系統(IBCS,Image-Based Classification System)、相冊預覽系統(PAPS,Photo Album Preview System)。
為什麼假設兩套?主要是為了分析對比各種授權模式的適用場景。
場景假設 A:基於圖像的物品分類系統(IBCS,Image-Based Classification System)
假設團隊正在開發一套“基於圖像的物品分類系統(Image-Based Classification System)”,該系統採用 RESTful API 對外交互,主要功能是允許用户通過 H5 應用或 APP 上傳圖片,系統分析後返回分類結果。為了表演 OAuth2 體系認證、授權、鑑權和權限控制的全流程,我們構建一個最簡化的 IBCS 演示項目:
服務名 | 類別 | 描述 | 技術選型
-
ibc-service 內部服務 資源服務器角色,圖像分類服務 Spring Boot 開發的 RESTful 服務 idp 內部服務 授權服務器角色,具體指負責認證、授權和鑑權 Spring Boot 開發 demo-h5 外部應用 demo 應用的前端 Antd-Pro 開發 demo-service 外部應用 demo 應用的後端 Spring Boot 開發
這裏設計了一個 demo 應用,對於 IBCS 來説,demo 應用就是一個外部應用,是一個消費者,包括前端 demo-h5 和後端 demo-service。但是從整個演示項目來説,ibc-service、idp、demo-h5 和 demo-service 都是自家應用,是相互信任的。
值得注意的是,在現實世界裏,應用有兩種類型:一是有 Server 端的應用;另一種是無 Server 端的應用。前者比較好理解,它可能是前後端分離的 MVC 單體應用,也有可能是 REST 型的前後端應用,這些都是常見的開發結構。除此之外,還有一些沒有或者不需要後端的應用,比如 SinglePage H5,由 Vue 或 React 開發的簡單 H5,整個應用僅由 JavaScript 代碼組成,前端即應用的全部。這種類型的應用,有一個最大的安全問題,即 client_secret 如何安全存儲,在無 Server 場景中無論是經典的授權碼模式還是密碼模式,都無法有效解決這個問題,因為一個全部由 JavaScript 寫成的程序,本質上是完全透明的,client_secret 存在泄露的風險。client_secret 將導致客户端被偽造,即攻擊者在拿到 client_id 和 client_secret 後偽造一個 App 發起虛假請求。當然許多平台會在 idp 端增加 redirect_uri 的綁定或 IP/域名白名單機制,從而有效降低偽造/劫持等安全風險。
有人説 OAuth 2.0 規範提出的 PKCE(Proof Key for Code Exchange by OAuth Public Clients)協議可以解決這個問題,這是一個錯誤的觀點,PKCE 在技術原理上並不能解決 client_secret 泄露的風險,所以對安全性要求高的情況下最好不要採用無 Server 端方式。不過 PKCE 作為一種增強協議可以搭配 OAuth2 組合使用以提高整體安全性。其主要流程為:由暴露在外的“前端”自身生成一串隨機的驗證碼,再根據驗證碼生成挑戰碼,然後用挑戰碼向授權服務器請求授權碼,授權服務器保存挑戰碼後返回授權碼給“前端”,“前端”拿到授權碼後用第一步生成的隨機驗證碼+授權碼向授權服務器申請令牌,授權服務器拿到驗證碼+授權碼後根據此驗證碼生成挑戰碼,如果生成的挑戰碼跟上一步保存的挑戰碼一致則頒發令牌。有興趣的同學可以自行查閲相關資料。
場景假設 B:相冊預覽系統(PAPS,Photo Album Preview System)
PAPS 是一個社交平台的子系統,與 IBCS 類似,採用 RESTful API 對外交互,主要功能是允許用户預覽自己的相冊,我們構建一個最簡化的 PAPS 演示項目:
服務名 | 類別 | 描述 | 技術選型
-
photo-service 內部服務 資源服務器角色,相冊預覽服務 Spring Boot 開發的 RESTful 服務 idp 內部服務 授權服務器角色,具體指負責認證、授權和鑑權 Spring Boot 開發 demo-h5 外部應用 demo 應用的前端 Antd-Pro 開發 demo-service 外部應用 demo 應用的後端 Spring Boot 開發
兩個演示案例唯一的不同就在於核心服務不同,一個是圖像分類服務,一個是相冊預覽服務。看起來似乎沒什麼差別,實際上他們的業務模型還很不同的,接下來會分析這一點。
授權碼模式 vs 密碼模式 vs 客户端模式
大家思考一下,授權碼模式、密碼模式和客户端模式,在不同的業務場景下該如何選擇?
無非有兩種做法:
- 不管三七二十幾,整個項目只使用一種授權模式,簡單明瞭;
- 仔細分析各種業務場景,給予合適的模式選型。
第一種做法,簡單粗暴,看起來好像不可取,但也不能一棍子打死,具體還是看項目的業務模型、設計目標和具體要求。但僅從科學嚴謹或者技術研究角度來説,肯定是第二種方式更加可取。接下來,我們就以第二種做法展開,詳細分析下模式選型的問題。
授權碼模式和密碼模式
我們先來看授權碼模式和密碼模式之間的比較,大家知道,授權碼模式是 OAuth2 體系安全性最高的模式,密碼模式與其相比,主要差別是少了一層用户確認授權的動作,缺乏這一動作就導致在授權階段,用户需要把用户名密碼告知客户端,造成潛在的密碼泄露風險。我們看一下對比:
比較項 | 授權碼模式 | 密碼模式
-
適用場景 不可信/第三方認證和授權、可信/內部服務認證和授權 可信/內部服務認證和授權 開發難度 較為複雜 相對簡單 安全性 最高 只要不運用於不可信/第三方場景則同樣安全
在這裏,可信/內部服務場景的定義是相對的概念,指納入同一套 OAuth2 體系的應用和服務,且這些應用和服務是由相同的或者相互信任的團隊開發,也可以稱作第一方應用。打個比方,微服務架構下的 B2C 商城系統,基本組成有前端 H5、無線端 APP、API 網關、認證授權服務、訂單服務、商品服務等,由於上述所有組成部分都同屬於一套 OAuth2 體系,且都由相同團隊開發,那麼他們全都歸屬於可信/內部服務場景。
那麼為什麼又説是相對概念?我們把視角聚焦到整個商城系統,毫無疑問,網關屬於安全邊界,網關以內的認證授權服務、訂單服務、商品服務屬於內部服務,而前端 H5、無線端 APP 則屬於外部應用,如果這些外部應用是其他團隊開發,我們也可以定義它們為第三方應用。這樣就以網關為邊界,劃分出了內部服務和外部服務,這就是所説的相對概念。
那麼,電商系統的前端 H5、無線端 APP 在認證和授權階段,採用授權碼模式和採用密碼模式,有什麼差別嗎?授權碼模式有一層用户確認授權的動作,從而避免泄露用户名和密碼給第三方應用,除此之外,兩者之間幾乎提供了相同的安全流程。這裏所指的第三方應用不正是前端 H5 和無線端 APP 嗎?都是自家開發的應用,自然是可信的,因此無須擔憂泄露不泄露的風險。
以此來看,在上述條件的限定下,兩種模式的安全性打平了。遵循“簡化原則”,採用密碼模式即可,當然喜歡挑戰複雜的同學選擇授權碼模式也是可以的。
但是也不是説授權碼模式就可以被密碼模式取代了,授權碼模式主要的應用場景,是在第三方/不可信應用的登錄和授權,主要解決在不泄露用户密碼的情況下如何安全授權某個應用向另一個應用提供用户資源的問題,舉例來説,某第三方應用(客户端)需要獲得用户(資源所有者)在另一個不可信應用(資源服務器)上的該用户的用户數據(資源)的場景就特別適合採用授權碼模式。
綜上,選擇授權碼模式還是密碼模式,具體要根據業務場景來確定,其中的關鍵決策點是應用或服務之間是否互相信任。
客户端模式
PAPS 相冊預覽系統演示案例應該採用何種授權模式?
回答這個問題之前,大家先思考一個問題:在 PAPS 中,資源所有者所指代的對象是什麼?
首先要明確資源是什麼,其次該資源是受保護的,最後資源歸誰所有,誰就是資源所有者。在 PAPS 中,很明顯受保護的資源是用户的相冊,資源所有者自然是用户本人。
明確資源所有者的含義後,再根據前文的分析,毫無疑問:如果 PAPS 的 demo 應用是第三方的不可信應用,則應該採用授權碼模式;如果是第一方可信應用,則可以採用密碼模式,當然不怕麻煩也可以用授權碼模式。
接下來,我們來分析 IBCS 圖片分類系統演示案例該用何種模式。
同樣地,回答這個問題之前,大家再思考一下:在 IBCS 中,資源所有者所指代的對象是什麼?
首先資源所有者所指代的對象不是一成不變的。在 IBCS 演示案例中,demo 應用向 ibc-service 傳送一張圖片,並希望返回分類結果,那麼這裏面的受保護資源具體是什麼呢?不似 PAPS 相冊預覽服務有實體資源概念,其受保護資源是用户的相冊,而 IBCS 難以抽象出一個實體的資源來。
可以這麼理解,IBCS 提供的核心能力是圖片分類算法,這就是它的受保護資源,圖片分類算法的所有權人顯然是持有此算法的實體組織或個人,因此資源所有者是該實體組織或個人。那麼矛盾點來了,以密碼模式為例,按照 OAuth2 的設計,資源所有者向客户端提供用户名和密碼,客户端將 client_id 和 client_secret 連同該用户名和密碼,向授權服務器申請令牌,此處的資源所有者是 IBCS 的普通用户,但是按照剛才所述,資源所有者應該是持有圖片分類算法的實體組織或個人,這不就矛盾了嗎?
因此,從資源所有者的角度來分析,IBCS 演示案例不適合採用授權碼模式,也不適合採用密碼模式,客户端模式才是最佳選擇。IBCS 並沒有用户的資源,授權碼模式和密碼模式都是需要用户授權才能跑通的,而 IBCS 提供的資源或服務並不屬於用户,所以法理上來説不需要用户的授權,IBCS 提供的分析服務是跟用户無關的。
以上就是選擇何種授權模式的一種分析模型,從資源所有者的維度切入。總結一下:
案例 | 場景 | 適合模式
-
IBCS 圖像分類系統案例 可信/內部服務 客户端模式 IBCS 圖像分類系統案例 不可信/外部應用 客户端模式 PAPS 相冊預覽系統案例 可信/內部服務 密碼模式 PAPS 相冊預覽系統案例 不可信/外部應用 授權碼模式
其實資源所有者的具體指代對於實際開發來説,並不是一個重要的方面,關鍵是開發人員要有自己的理解,能夠靈活巧用。説句題外話,雖然 OAuth2 是當今領域內的權威,但我們也不應盲目地無條件相信權威,技術發展一定是有漏洞和不完美之處的。
密碼模式的典型架構層次和主要流程
我們以 PAPS 相冊預覽系統為例,介紹密碼模式的架構層次和主要流程。
如圖所示,是密碼模式的最精簡架構層次,在實際開發中可以此作為基礎進行擴展。密碼模式涉及到五種主要角色,另外還有一個用户代理/瀏覽器角色:
- 用户代理/瀏覽器:一般單體應用都是前後端分離的 MVC 結構,從這個角度看,這裏具體可以將用户代理/瀏覽器理解為前端的 H5 應用或者無線端的 APP,換句話説 H5/APP 承載用户的交互操作而成為用户代理。具體到 PAPS 演示案例就是 demo-h5;
- 客户端:具體指單體應用的後端服務,具體到 PAPS 演示案例就是 demo-service;
- 授權服務器:具體指負責認證、授權和鑑權的 IDP(Identify Provider),也是 OAuth2 體系的核心服務,也可以簡單叫做 auth-service。具體到 PAPS 演示案例就是 idp;
- 受保護資源:即資源服務器,一般是內部服務,比如產品服務、訂單服務等。具體到 PAPS 演示案例就是 photo-service;
- 資源所有者:顧名思義,即受保護資源的所有者,一般具體指代用户自己。具體到 PAPS 演示案例就是用户。
整個流程分為兩個階段:
- 第一階段:認證授權階段
- 用户代理(demo-h5)將用户輸入的用户名和密碼,發送給客户端(demo-service);
- 客户端(demo-service)將用户輸入的用户名和密碼,連同 client_id + client_secret (由 idp 分配)一起發送到 idp 以請求令牌,如果 idp 約定了 scope 則還需要帶上 scope 參數;
- idp 首先驗證 client_id + client_secret 的合法性,再檢查 scope 是否無誤,最後驗證用户名和密碼是否正確,正確則生成 token。這一步也叫“認證”;
- idp 返回認證結果給客户端,認證通過返回 token,認證失敗返回 401。如果認證成功則此步驟也叫“授權”;
- 客户端收到 token 後進行暫存,並創建對應的 session;
- 客户端頒發 cookie 給用户代理/瀏覽器。
至此,認證授權階段完成。其中步驟 5-6 也有其他會話方案,比如 REST 型應用可能會將 token 存儲在瀏覽器端,但 session/cookie 方案無疑是最穩妥的選擇。
在一般的 Web 應用中,可以將此階段看作是一次用户登錄過程。
- 第二階段:授權後請求資源階段
- 用户通過用户代理(demo-h5)訪問“我的相冊”頁面,用户代理攜帶 cookie 向客户端(demo—service)發起請求;
- 客户端通過 session 找到對應的 token,攜帶此 token 向資源服務器(photo-service)發起請求;
- 資源服務器(photo-service)向 idp 請求驗證 token 有效性;
- idp 校驗 token 有效性,再根據 scope 判斷客户端(demo-service)是否有權限調用此 API,最後返回校驗結果給資源服務器。這一步也叫“鑑權”;
- 資源服務器根據 idp 檢驗結果(true/false 或其他等效手段)決定是否返回用户相冊數據給客户端。如果 token 校驗失敗則返回 401 給客户端,如果 scope 檢查不通過則返回 403。這一步也叫“權限控制”。
至此,授權後請求資源階段完成。
事實上 scope 參數不是核心的內容,實際工作中為了簡化開發步驟甚至可以忽略它。scope 參數是用來約束客户端的權限的,跟用户權限(authorities)是不同的。比如可以在 idp 中利用 scope 參數約束某客户端只能發起讀(GET)型請求,或只能調用指定的幾個 API 等,具體業務邏輯自行編寫。
密碼模式的微服務架構層次和主要流程
我們仍以 PAPS 相冊預覽系統為例,介紹密碼模式在微服務場景下的架構層次和主要流程。
微服務場景下,增加了一個網關,網關實際上是一個反向代理,將用户的請求轉發到內部服務器。類似地,微服務場景下也分為兩個階段,而且第一階段沒什麼變化,主要不同在於第二階段:
- 用户通過用户代理(demo-h5)訪問“我的相冊”頁面,用户代理攜帶 cookie 向客户端(demo—service)發起請求;
- 客户端通過 session 找到對應的 token,攜帶此 token 向網關發起對資源服務器(photo-service)的請求;
- 網關截取 token 連同本次請求的細節,一併向 idp 請求校驗;
- idp 校驗 token 有效性,再根據 scope 和請求細節判斷客户端(demo-service)是否有權限調用此 API,最後返回校驗結果給網關。如果校驗全部通過,idp 生成 JWT 並返回給網關;如果 token 校驗失敗返回 401;如果 scope 檢查不通過則返回 403;
- 如果校驗通過,網關將得到 JWT,攜帶此 JWT 轉發請求到資源服務器;
- 資源服務器解析 JWT 得到用户信息,查詢用户相冊數據後返回給網關;
- 網關將用户相冊數據返回給客户端。
此流程有兩項重大變化:一是加入網關使得整個流程複雜了一些;二是網關以內使用 JWT 作為令牌。關於這兩點的解讀,本文不再贅述,感興趣的同學可以參閲本人早前的文章《微服務架構下的統一身份認證和授權》。
值得注意的是,步驟 9-11,還有另一種處理方法,即將 scope 客户端權限檢查放到網關進行:
- 網關截取 token 後向 idp 請求校驗;
- idp 校驗 token 有效性,通過校驗則根據 token 查詢用户信息和 scope,生成 JWT 返回給網關;如果不通過則返回 401;
- 網關得到 JWT,解析後根據 scope 判斷客户端是否有權限調用此 API,如有則攜帶 JWT 轉發請求到資源服務器,否則返回 403 給客户端。
客户端權限檢查放到網關,則網關要維護 scope 和客户端權限的邏輯。
客户端模式的微服務架構層次和主要流程
我們以 IBCS 圖片分類系統為例,介紹客户端模式在微服務場景下的架構層次和主要流程。
可以看到,客户端模式流程比較簡單,這裏就不再敍述具體流程了,不過請注意第 2 步:
- 客户端用向 idp 申請令牌之前,應該先檢查是否緩存了有效令牌,有的話直接跳到第 6 步發起服務訪問請求。
授權碼模式的微服務架構層次和主要流程
我們仍以 PAPS 相冊預覽系統為例,介紹授權碼模式在微服務場景下的架構層次和主要流程。
整個流程分為兩個階段:
- 第一階段:認證授權階段
- 用户在用户代理(demo-h5)處點擊登錄按鈕,或請求授權登錄按鈕,此操作將訪問客户端的某個 URI;
- 客户端(demo-service)將用户導向 idp 提供的認證授權頁面,並在頁面 ULR 參數中攜帶 client_id,response_type=code,redirect_uri(可選),scope(可選),state(可選);
- 用户通過用户代理(demo-h5),在 idp 的認證授權頁面選擇是否給予授權,如用户未登錄,則需要先登錄後再操作;
- 用户給予授權,idp 將用户導向 redirect_uri 指定的頁面,並附加授權碼(code);如果未指定 redirect_uri,則導向發起該請求時的 URI,同時附加授權碼(code);
- 客户端收到授權碼(code),向 idp 發起令牌申請,同時附上 client_id(必填) + client_secret(必填) + state(如有) + scope(如有)。注意這一步是客户端在後台發起的,用户層面無法感知;
- idp 收到請求後,先核對 client_id + client_secret + scope(如有)是否無誤,然後校驗授權碼(code),全部正確後頒發 token 給客户端。
- 第二階段:授權後請求資源階段
該階段的流程,與密碼模式的微服務場景流程一致,此處不在贅述。
對於 REST 型 demo 應用
如果 demo 應用是一個 REST 型應用,則在第 1、2 步驟中,還可以這麼處理:
- 用户在用户代理(demo-h5)處點擊登錄按鈕或請求授權登錄按鈕後,通知客户端(demo-service),客户端收到通知後返回重定向的指示,以及 scope(可選),state(可選)等;
- demo-h5 收到響應後,直接將用户導向 idp 提供的認證授權頁面,並在頁面 URL 參數中攜帶客户端返回的參數(除了 state 參數外,其他參數可以在 demo-h5 中寫死)client_id,response_type=code,redirect_uri(必選),scope(可選),state(可選),其中 redirect_uri 建議是必選的,而且必須是客户端提供的 URI(回調地址)。
還可以這麼處理:
- 用户在用户代理(demo-h5)處點擊登錄按鈕或請求授權登錄按鈕後,直接將用户導向 idp 提供的認證授權頁面,並在頁面 URL 參數中攜帶 client_id,response_type=code,redirect_uri(必選),scope(可選),但是不需要攜帶 state 參數,因為客户端不知道此參數的存在,其中 redirect_uri 建議是必選的,而且必須是客户端提供的 URI(回調地址)。
至此,授權碼模式的認證授權全流程完畢。
討論:客户端第一次將用户導向 idp 提供的認證授權頁面時,idp 是否需要驗證客户端的身份呢?或者説需不需要提供 client_secret 呢?
微服務網關的負載問題
在微服務場景下,大量的請求和響應都經過網關轉發,我們設想一下,如果類似 PAPS 相冊系統那樣,返回的是圖片數據,而且不採用 CDN 分發網絡或文件存儲服務等技術,那麼圖片流通過網關再返回給用户,網關的負載是不是將會非常龐大呢?
當然,網關本身可以做負載均衡,可以引入緩存,數據流可以做 CDN 處理等,這些都是非常好的高性能方案,除此之外,有沒有其他辦法呢?
這裏引入一個網絡技術領域的概念——負載均衡有三種模式:反向代理、透傳模式和三角模式。這裏簡單介紹一下三角模式:
假設一種網絡結構,包括 Web 服務器、PC 客户端和負載均衡器。PC 通過負載均衡器發起對 Web 服務器的訪問。在三角模式下的訪問流程如下:
- PC 向負載均衡器發起對 Web 服務器的訪問請求;
- 負載均衡器將 PC 的請求轉發給 Web 服務器;
- Web 服務器收到請求後,直接將響應數據發送給 PC 端。
PC、Web 服務器和負載均衡器三者呈三角形狀,因此叫做三角模式。這個模式的優點就是負載均衡器只負責轉發請求,而響應數據包則不需要接收和轉發,從而大副降低負載均衡器自身的數據流通壓力。三角模式也類似於 LVS 的 DR 模式,LVS 調度器只負責接收和轉發請求,後端的集羣服務器返回的真實數據包將直接發往請求的客户端。
按照這個思路,我們可以將三角模式引入網關,從而大副降低網關的負載壓力。
令牌的複用問題
我們設想一個場景,團隊研發的平台同時包含了 IBCS 圖片分類服務和 PAPS 相冊預覽服務,那麼用户在登錄平台(用密碼模式認證授權)後,先訪問“我的相冊”,然後從中選擇一張照片發起物品識別的請求。
根據文章的模式選型分析,IBCS 服務應採用客户端模式,PAPS 服務應採用密碼模式,那麼,客户端是否應該申請兩套令牌?
回答這個問題,我們還是要從具體場景切入分析:
- 如果用户需要登錄平台後才能使用 IBCS 和 PAPS 的服務,那麼,只需要用密碼模式一種令牌即可;
- 如果 PAPS 功能無需登錄,遊客也能使用,那麼密碼模式和客户端模式要分開處理。
授權碼模式是最嚴格的,密碼模式次之,客户端模式最差,因此一般情況下,授權碼模式的令牌可以給其他模式使用,密碼模式令牌可以給客户端模式使用,客户端模式只能自己使用。
三、Spring 家族 OAuth2 相關組件概覽
好了,從本節開始我們脱離枯燥的理論環節,進入一樣枯燥的實戰開發頻道。
目前構建 OAuth2 授權系統有三種方式:一是基於主流的開源組件構建;二是接入第三方授權服務(如 Google、GitHub OAuth2);三是根據 OAuth2 的標準規範自行開發相關授權組件。
常用的開源組件有 RedHat Keycloak、Spring Security、Spring Security OAuth2,以及剛起步的 Spring Authorization Server 等。值得一提的是 RedHat Keycloak,它是一款開源、成熟的 IAM 解決方案,功能強大且可私有化部署。
在 Spring 的開發生態裏,建議採用 Spring Security 方向的開源組件方案,可擴展性好,定製性強,用户廣泛且社區活躍。
Spring Security OAuth2 目前已停止更新,官方不推薦繼續使用。相關功能已經遷移到 Spring Security,但授權服務器(Authorization Server)功能並未包含。Authorization Server 功能將由 Spring Security 團隊主導開發的 Spring Authorization Server 開源組件提供,詳細信息請查看官方通告:https://spring.io/blog/2020/0...
Spring Authorization Server 項目目前還在迭代中,該項目的開發計劃託管在 ZenHub 上,感興趣的同學可以自行了解: https://app.zenhub.com/worksp...
一)基於 Spring Framework
- spring-security
核心組件,當前幾乎所有 Spring OAuth2 開源技術方案都依賴於它。核心模塊: spring-security-core。
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>5.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>5.5.1</version>
</dependency>
</dependencies>
- [已停止更新] spring-security-oauth2
依賴於 spring-security。該組件現已合併到 spring-security 中,官方已不建議使用。在此之前是 Spring Security 團隊官方維護的唯一且被廣泛使用的組件。
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.5.1.RELEASE</version>
</dependency>
- spring-security-oauth2-authorization-server
依賴於 spring-security。在 Spring Security 官方宣佈停止 spring-security-oauth2 組件更新後,推出的授權服務器 Authorization Server 的替代組件。 spring-security 整合 spring-security-oauth2 後,並不包括 Authorization Server 功能,因此如果需要開發此功能,則要搭配 spring-security-oauth2-authorization-server 一起使用。
<!-- https://mvnrepository.com/artifact/org.springframework.security.experimental/spring-security-oauth2-authorization-server -->
<dependency>
<groupId>org.springframework.security.experimental</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.1.2</version>
</dependency>
二)基於 Spring Boot
- spring-boot-starter-security
依賴於 spring-security。相當於 spring-boot + spring-security ,默認包含 SecurityAutoConfiguration.class ,因此會執行一些自動化配置,可以簡化開發步驟。如果想關閉自動配置,可以修改 Spring Boot 啓動註解為 @SpringBootApplication(exclude = { SecurityAutoConfiguration.class })
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
三)基於 Spring Cloud
- spring-cloud-starter-oauth2
依賴於 spring-boot-starter-security + spring-security-oauth2,並額外提供了許多功能實現,在微服務場景下比較實用。
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR12</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
三、核心組件選型
最佳方案: spring-boot-starter-security + spring-authorization-server
由於 spring-security-oauth2 已經遷移到 spring-security,而 spring-boot-starter-security 集成了 spring-security,且做了許多簡化配置,特別適合於構建 spring-boot 程序。截至 2021年8月,spring-authorization-server 的最新版本是 0.1.2,最新的消息請關注官方動態 https://spring.io/blog/2020/0...
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security.experimental/spring-security-oauth2-authorization-server -->
<dependency>
<groupId>org.springframework.security.experimental</groupId>
<artifactId>spring-security-oauth2-authorization-server</artifactId>
<version>0.1.2</version>
</dependency>
Spring Authorization Server 項目遵守 OAuth2.1 規範而不支持 password 模式,因此需要是用此模式的項目可以考慮備選方案。
https://app.zenhub.com/worksp...
備選方案: spring-security-oauth2 或 spring-cloud-starter-oauth2
此方案採用了 spring-security-oauth2,已被 Sprnig Security 官方停止更新,因此不再推薦。另外請注意的 spring-security-oauth2 2.4.0.RELEASE 及之後的版本會提示 deprecated。
- 如果採用 spring-security-oauth2,則 pom 引用如下:
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth/spring-security-oauth2 -->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.8.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.security.oauth.boot/spring-security-oauth2-autoconfigure -->
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<!-- spring-security-oauth2-autoconfigure 2.1.3.RELEASE 及之後的版本會提示 deprecated。
- 或採用 spring-cloud-starter-oauth2,則 pom 引用如下:
spring-cloud-starter-oauth2 集成了 spring-security-oauth2,且做了許多簡化配置,是在很長一段時間裏基於開源軟件構建 OAuth2 的主要方案之一。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>