使用 OIDC 在一個 Keycloak 中集成另一個 Keycloak 用戶認(rèn)證
這是一篇價值 7 元的文章,有人已經(jīng)付費,免費分享給你!

周末有人在知乎上通過付費咨詢頻道問我,如何在一個 Keycloak 中集成另一個 Keycloak 用戶認(rèn)證。由于目前知乎付費咨詢只支持手機端,因此我當(dāng)時的回答比較簡略的。今天再用電腦把實現(xiàn)步驟一步一步寫下來,爭取讓小白也能夠順利集成,因此文章會略顯啰嗦,高手請選擇性地跳躍閱讀或者直接忽略。


問題重述
我看了你關(guān)于《基于 keycloak 的關(guān)注公眾號即登錄功能的設(shè)計與實現(xiàn)》的文章,有點問題想和你溝通下。
我現(xiàn)在有2個系統(tǒng),a和b。它們都接入了keycloak,是2個realm。數(shù)據(jù)庫也是隔離的?,F(xiàn)在想要實現(xiàn)a系統(tǒng)通過b系統(tǒng)的賬號登錄。我想用類似微信登錄的方式,把b系統(tǒng)當(dāng)成微信。不知道這樣是否合適,以及具體的實現(xiàn)感覺有點困難。
最終實現(xiàn)
完全可行,不用一行代碼。
在線演示
在《基于 keycloak 的關(guān)注公眾號即登錄功能的設(shè)計與實現(xiàn)》里已經(jīng)搭建了一個 Keycloak 站點:https://keycloak.jiwai.win。在這名同學(xué)的問題里,即是系統(tǒng) b。
為了演示這名同學(xué)的系統(tǒng) a,我搭建了另一個 Keycloak 站點:https://lemur-2.cloud-iam.com/。你可以點擊這里,然后選擇使用系統(tǒng) b 登錄,就可以看到效果:

顯然,Keycloak a 是一個剛部署好的 Keycloak,默認(rèn)只支持用戶名密碼登錄。但是為了使用 Keycloak b 登錄,所以又增加了一個登錄方式:UniHeart At Jiwai Win,這就是 https://keycloak.jiwai.win。選擇這個登錄方式,頁面跳轉(zhuǎn)到了 Keycloak a 的登錄界面(如果看過前面的《基于 keycloak 的關(guān)注公眾號即登錄功能的設(shè)計與實現(xiàn)》,對這個界面很熟悉了吧)。

使用 Keycloak b 的任何登錄方式(正好這個 Keycloak b 支持關(guān)注微信公眾號即登錄功能,但這個不是必須的),成功登錄后,都會回到 Keycloak a,并且是已經(jīng)登錄狀態(tài):

雖然頁面顯示用戶沒有權(quán)限查看該頁面,但是說明登錄已經(jīng)成功。關(guān)于如何給該用戶配置相關(guān)權(quán)限,可以自行探索。
如果已管理員賬號登錄 Keycloak a,在用戶管理界面可以看到已經(jīng)多了一個 Keycloak b 中的賬號:

問題分析
這名同學(xué)希望將兩個不同的 Keycloak realm 用戶打通,可以使用 Keycloak realm b 登錄 Keycloak realm a?;蛘哒f,使用 Keycloak b 登錄 Keycloak a,這是等價的。為了說明問題,我們不如建立兩個 2 個 Keycloak 實例,不僅 realm 不同,整個 Keycloak 實例都不同(數(shù)據(jù)庫也是獨立的)。
另外,這名同學(xué)看了之前的《基于 keycloak 的關(guān)注公眾號即登錄功能的設(shè)計與實現(xiàn)》一文,可能受到微信登錄的影響。其實他這里想表達(dá)的和微信登錄沒有任何關(guān)系。
基礎(chǔ)知識回顧
正如前面所說,整個實現(xiàn)過程不需要一行代碼,而且配置步驟也非常簡單。但是在實施過程中,為什么會感到困難呢?很可能是需要補充一些基礎(chǔ)知識。這里在詳細(xì)列出實現(xiàn)步驟前,對相關(guān)知識做一個簡單回顧,從而在閱讀詳細(xì)步驟時,不僅知其然,還能知其所以然。
單點登錄
Keycloak 是一個優(yōu)秀的開源的單點登錄工具。想使用系統(tǒng) B的用戶直接登錄系統(tǒng) A,而不用再次去系統(tǒng) A 里注冊,這也是典型的單點登錄場景。單點登錄有多種協(xié)議:
單點登錄認(rèn)證協(xié)議
最知名的單點登錄認(rèn)證協(xié)議主要是 OpenId Connect 和 SAML。下面就來看看認(rèn)證服務(wù)器和被保護(hù)的應(yīng)用是怎么和這些協(xié)議打交道的。
OpenId Connect
OpenId Connect 通常簡稱為 OIDC,它是在 OAuth 2.0 的基礎(chǔ)上擴展而成的認(rèn)證協(xié)議。OAuth 2.0 只是一個構(gòu)建認(rèn)證協(xié)議的框架,并且很不完整,但是 OIDC 確實一個羽翼豐滿的認(rèn)證與授權(quán)協(xié)議。OIDC 嚴(yán)重使用 Json Web Token(JWT)標(biāo)準(zhǔn)。這些標(biāo)準(zhǔn)用緊湊和對網(wǎng)絡(luò)友好的方式定義了 JSON 格式的唯一標(biāo)記以及對數(shù)據(jù)進(jìn)行數(shù)字化簽名和加密的方法。
OIDC 在 Keycloak 中的使用場景分為兩種類型。第一種是應(yīng)用請求 Keycloak 服務(wù)器來認(rèn)證用戶。當(dāng)成功登錄后,應(yīng)用會收到一個名稱為 access_token 的唯一身份標(biāo)志符。這個唯一身份標(biāo)志符包含了諸如用戶名、電子郵箱、以及其他個人資料等等信息。access_token 會被 realm 進(jìn)行數(shù)字簽名,并且包含用戶的可訪問信息(比如用戶-角色映射),從而應(yīng)用可以使用它來決定該用戶被允許訪問應(yīng)用中的哪些資源(上面的在線演示中顯示的已登錄用戶沒有權(quán)限頁面,就是因為拿到的 access_token 中沒有相關(guān)的可訪問信息)。
第二種使用場景類型是客戶端想獲取遠(yuǎn)端服務(wù)的訪問權(quán)限。在這個場景下,客戶端請求 Keycloak 來獲取一個訪問令牌來代表用戶調(diào)用遠(yuǎn)端服務(wù)。Keycloak 認(rèn)證該用戶后詢問用戶是否同意為該客戶端授予訪問權(quán)限。一旦用戶同意授權(quán),客戶端就會收到訪問令牌。這個訪問令牌是由 realm 數(shù)字化簽名過的。該客戶端隨后就可以使用這個訪問令牌向遠(yuǎn)端服務(wù)發(fā)起 REST 調(diào)用了。這個 REST 服務(wù)抽取出訪問令牌,驗證令牌的簽名,然后基于令牌中的可訪問信息決定是否保護(hù)這個調(diào)用請求。
OIDC 認(rèn)證流程
OIDC 有多種不同的方式為客戶端或者應(yīng)用提供用戶認(rèn)證并接收身份標(biāo)記和訪問令牌。你要使用那種方式很大程度上取決于應(yīng)用或者客戶端請求訪問權(quán)限的類型。所有這些認(rèn)證流程都在 OIDC 和 OAuth 2.0 的規(guī)格文檔中詳細(xì)描述了,所以這里只是稍稍提及一些必要內(nèi)容。
授權(quán)碼流程
這是一個基于瀏覽器的協(xié)議,也是在驗證和授權(quán)基于瀏覽器的應(yīng)用時所推薦使用的。它嚴(yán)重依賴瀏覽器重定向來獲取身份標(biāo)記與訪問令牌??偨Y(jié)如下:
1.使用瀏覽器訪問應(yīng)用。這個應(yīng)用會提醒用戶當(dāng)前還未登錄,所以它指示瀏覽器重定向到 Keycloak 來認(rèn)證。該應(yīng)用會以查詢參數(shù)的形式在瀏覽器重定向時向 Keycloak 傳遞一個回調(diào) URL(即演示截圖中的 redirect_uri),Keycloak 在完成認(rèn)證后會使用到它。
2.Keycloak 認(rèn)證用戶,并創(chuàng)建一次性、非常短時間有效的臨時碼。Keycloak 通過前面提供的回調(diào) URL 重定向回到應(yīng)用,同時將臨時碼作為查詢參數(shù)附加到回調(diào) URL 上。
3.應(yīng)用抽取臨時碼,并且在后端通過不同于前端的網(wǎng)絡(luò)渠道向 Keycloak 發(fā)起 REST 調(diào)用,使用臨時碼交換身份標(biāo)記、訪問令牌以及刷新令牌。一旦這個臨時碼在獲取令牌中被使用過了,它就不能再次被使用了。這防止了潛在的重放攻擊。
非常重要的一點是訪問令牌通常有效期很短,一般在分鐘級別過期。而刷新令牌由登錄協(xié)議傳送,允許應(yīng)用在訪問令牌過期后去獲取一個新的訪問令牌。這樣一個刷新協(xié)議在受損系統(tǒng)中非常重要。如果訪問令牌有效期很短,那么整個系統(tǒng)僅僅在被盜用的令牌剩余的有效期內(nèi)是處于被攻擊狀態(tài)的。如果管理員吊銷了訪問權(quán)限,那么接下來的令牌刷新請求會失敗。這樣更加安全并且可伸縮性更好。
該流程另一個重要的方面是所謂的開放客戶端還是保密客戶端的概念。保密的客戶端在使用臨時碼交換令牌時需要提供客戶端密鑰。開放客戶端則不需要。只要嚴(yán)格使用 HTTPS 并且客戶端的重定向 URI 被嚴(yán)格注冊,那么采用開放客戶端完全沒有問題。由于無法使用安全的方式傳輸客戶端密鑰,所以 HTML5/JavaScript 客戶端不得不天然就屬于開放客戶端。再次強調(diào),這僅僅在嚴(yán)格使用 HTTPS 并嚴(yán)格注冊重定向 URI 時是可以的。
隱式流程
這也是一個基于瀏覽器的協(xié)議,很類似授權(quán)碼流程,只是請求量更少,也不需要刷新令牌。該流程不被推薦,因為存在訪問令牌泄漏的可能性。比如由于令牌通過重定向 URI (詳見下)傳輸,所以可能通過瀏覽器歷史記錄泄漏。并且,由于該流程沒有為客戶端提供刷新令牌的服務(wù),所以訪問令牌不得不設(shè)置一個更長的時間,不然當(dāng)令牌失效后用戶需要再次認(rèn)證。Keycloak 不推薦這種流程但是仍然支持這種流程,因為它存在于 OIDC 和 OAuth 2.0 的規(guī)格文檔中??偨Y(jié)如下:
1.使用瀏覽器訪問應(yīng)用。應(yīng)用提示用戶當(dāng)前還未登錄,所以它指示瀏覽器重定向到 Keycloak 去認(rèn)證。應(yīng)用將回調(diào) URL (一個重定向 URI)作為查詢參數(shù)傳遞給 Keycloak,在認(rèn)證完成后會被其使用。
2.Keycloak 認(rèn)證用戶并且創(chuàng)建身份標(biāo)記和訪問令牌。Keycloak 使用之前提供的回調(diào) URL 重定向回到應(yīng)用并使用查詢參數(shù)的方式額外添加身份標(biāo)記和訪問令牌在回調(diào) URL zhong。
3.應(yīng)用從回調(diào) URL 中抽取身份標(biāo)記和訪問令牌。
資源擁有者密碼憑據(jù)授權(quán)(直接訪問授權(quán))
這在 Keycloak 管理員控制臺中指直接訪問授權(quán)。當(dāng) REST 客戶端希望代表用戶獲取令牌時使用該流程。這是一個 HTTP POST 請求,該請求中包含了用戶的安全憑據(jù)和客戶端 id,以及客戶端的密鑰(如果是保密客戶端的話)。該用戶的安全憑據(jù)隨請求中的表單參數(shù)發(fā)送。這個 HTTP 響應(yīng)中包含的身份標(biāo)記、訪問權(quán)限,以及刷新令牌。
客戶端憑據(jù)授權(quán)
這也是由 REST 客戶端使用的,但不是代表一個外部用戶去獲取令牌,而是基于和客戶端相關(guān)的元數(shù)據(jù)與服務(wù)賬號的權(quán)限來創(chuàng)建一個令牌。
Keycloak 服務(wù)器的 OIDC URI 端點
這一小節(jié)非常簡單,但是對本文來說,卻非常非常重要。因為在配置時,是直接要用到相關(guān)的端點的。
Keycloak 會公布一系列的 OIDC 端點。當(dāng)你使用客戶端適配器與認(rèn)證服務(wù)器進(jìn)行 OIDC 溝通時,這些 URL 非常有用。這些全部都是相對 URL,且其根 URL 是使用 HTTP(S) 協(xié)議的,并且會在其 hostname 的基礎(chǔ)上添加 /auth 路徑。比如,典型的根 URL 是:https://keycloak.jiwai.win/auth,或者 http://localhost:8080/auth。
/realms/{realm-name}/protocol/openid-connect/auth
在授權(quán)碼流程中這個 URL 端點用來獲取臨時碼,在隱式流程、直接授權(quán)或者客戶端授權(quán)中,這個 URL 端點用來獲取令牌。
/realms/{realm-name}/protocol/openid-connect/token
這個 URL 端點用來在授權(quán)碼流程中將臨時碼轉(zhuǎn)換成令牌。
/realms/{realm-name}/protocol/openid-connect/logout
這是用來執(zhí)行退出登錄操作的 URL 端點。
/realms/{realm-name}/protocol/openid-connect/userinfo
這個 URL 端點是用來提供用戶信息服務(wù)的,其詳細(xì)描述參見 OIDC 規(guī)格說明。
/realms/{realm-name}/protocol/openid-connect/revoke
這個 URL 端點用來做 OAuth 2.0 中的令牌吊銷,其詳細(xì)描述見 RFC7009。
SAML
SAML 2.0 類似 OIDC,但是產(chǎn)生得更早并且更加成熟。由于本文使用 OIDC 解決這名知乎用戶的問題,因此不對 SAML 做詳細(xì)介紹。
OpenID Connect 和 SAML 的對比
選擇 OpenID Connect 還是 SAML?并不推薦簡單的使用新的協(xié)議(OIDC)而不是用更老的但是更成熟的協(xié)議(SAML)這種一刀切的決策思路。
但是 Keycloak 在大多數(shù)情況下都推薦使用 OIDC,這也是本文解決知乎網(wǎng)友問題時的做法。
SAML 要比 OIDC 更加啰嗦一些。
除了交換數(shù)據(jù)更加啰嗦之外,如果你仔細(xì)對比規(guī)格說明文檔,你會發(fā)現(xiàn) OIDC 是圍繞 Web 相關(guān)的工作而設(shè)計的,但是 SAML 卻是在 Web 的基礎(chǔ)上增加了新的設(shè)施。比如,相對 SAML 來說,OIDC 在客戶端的實現(xiàn)更加容易,因此 OIDC 更加適合 HTML5/JavaScript 應(yīng)用。由于令牌是 JSON 格式的,他們更容易被 JavaScript 所消費。當(dāng)然,OIDC 還有其他好特性使得在 Web 應(yīng)用中實現(xiàn)安全更加容易。比如規(guī)格文檔中提到的,使用 iframe 技巧,就很容易探測用戶是否還處于登錄狀態(tài)。
當(dāng)然 SAML 也還是有其用武之地的。隨著 OIDC 的規(guī)格文檔的演進(jìn),你會發(fā)現(xiàn)它實現(xiàn)的越來越多的特性,早在幾年前 SAML 就已經(jīng)有了。人們一般使用 SAML 的原因是已有系統(tǒng)已經(jīng)使用了 SAML 加固,以及 SAML 更加成熟。
集成步驟
好了,說了這么多,是為了在后續(xù)實現(xiàn)步驟中,不迷失方向。實現(xiàn)步驟本身特別簡單,關(guān)鍵是需要了解這些基礎(chǔ)知識,否則就會覺得莫名其妙。
準(zhǔn)備工作:搭建兩個 Keycloak 系統(tǒng)
?Keycloak b,我們將用它來登錄其他系統(tǒng),包括 Keycloak a。處于免費的考慮,可以使用 Heroku 平臺。但是由于 Heroku 平臺的限制,不得不對 Keycloak 做小的改造。改造后的 Keycloak 我放在了 Github 上:https://github.com/Jeff-Tian/keycloak-heroku,你可以點擊 ReadMe 中的按鈕一鍵部署到 Heroku 上。比如我部署好的 Keycloak b 是:https://keycloak.jiwai.win 。
?Keycloak a,你同樣可以使用 Heroku 再部署一個實例。也可以利用 https://www.cloud-iam.com/ 提供的免費托管 Keycloak,它的限制是只能有 100 個用戶。比如我部署好的 Keycloak a 是 https://lemur-2.cloud-iam.com/。
在 Keycloak b 中注冊一個客戶端 Keycloak a
這很簡單,如下圖所示。關(guān)鍵配置已用紅色框圈起來。
用 Keycloak b 的管理員賬號密碼登錄 Keycloak b,在相應(yīng)的 Realm 中點擊新建一個客戶端,首先需要起個名字,比如命名為 UniHeart-Cloud-IAM。
然后需要在客戶端協(xié)議中選擇“openid-connect”。所以說基礎(chǔ)知識很重要,不然會在眾多選項里迷失方向。
隨后在訪問類型中,選擇保密(如前面的基礎(chǔ)知識里講的,如果能夠保證嚴(yán)格的 HTTPS 實施以及重定向 URI 的嚴(yán)格匹配,那么選擇開放也是可以的)。
最后,在重定向 URI 里配置好 Keycloak a 的重定向 URI(我填的是我在 Cloud IAM 中新部署的實例:https://lemur-2.cloud-iam.com/*),如果是選擇開發(fā)的訪問類型,那么這里的重定向 URI 必須一字不差。但是我這里選擇了保密的訪問類型,所以這里使用了通配符,保持靈活性。

保存好后,切換到安全憑據(jù)面板,復(fù)制客戶端密鑰,后續(xù)步驟需要用到:

注意在客戶端認(rèn)證中選擇第二項:客戶端 Id 和密鑰的方式,然后復(fù)制密鑰。
在 Keycloak a 中添加 Keycloak b 為一個身份認(rèn)證服務(wù)(idp)
完成 Keycloak b 中的配置工作后,現(xiàn)在回到 Keycloak a,即新部署的 Cloud IAM 實例,使用 Keycloak a 的管理員賬號密碼登錄,然后點擊添加一個身份認(rèn)證服務(wù),選擇 Keycloak openid connect 方式:

首先為這個身份認(rèn)證服務(wù)起個名字,比如 UniHeart At Jiwai Win

然后的重點就是配置 OpenID Connect 了,基礎(chǔ)知識又派上用場。這里最重要的是把 Keycloak b 服務(wù)器的 OIDC URI 端點中的
?/realms/{realm-name}/protocol/openid-connect/auth 以及
?/realms/{realm-name}/protocol/openid-connect/token
?/realms/{realm-name}/protocol/openid-connect/userinfo 三個端點配置進(jìn)去:

注意在客戶端認(rèn)證項里選擇以 POST 方式發(fā)送客戶端密鑰,并將 UniHeart-Cloud-IAM 填寫在客戶端 ID 中,同時將上一步復(fù)制好的密鑰粘貼進(jìn)入客戶端密鑰一欄。
完成
保存好就完成了。這時候點擊右上角,退出當(dāng)前管理員用戶,就進(jìn)入到了登錄頁面。你可以看到除了使用用戶名密碼登錄方式之外,已經(jīng)多了一個登錄選項,這就是使用 Keycloak b 登錄:

https://lemur-2.cloud-iam.com/auth/admin/uniheart/console/#/realms/uniheart/identity-provider-settings/provider/keycloak-oidc/uniheart-jiwai-win,這個鏈接,就是使用 Keycloak b 登錄 Keycloak a 的完整鏈接。

