原理分析SonarQube中IdentityProvider賬戶互斥現(xiàn)象
前言
soanr 是一個(gè)代碼質(zhì)量管理系統(tǒng),代碼是開(kāi)源的。在公司統(tǒng)一認(rèn)證平臺(tái)還沒(méi)出來(lái)時(shí),sonar 已接入 ldap 提供系統(tǒng)登錄認(rèn)證功能,現(xiàn)在使用 sonar-auth-oidc 插件以 OIDC 協(xié)議接入集中認(rèn)證平臺(tái)時(shí),發(fā)現(xiàn)用戶的賬戶是互斥的(如果現(xiàn)有用戶之前采用 ldap 登錄,使用 oidc 登錄后會(huì)創(chuàng)建一個(gè)新的用戶,沒(méi)法關(guān)聯(lián)之前的賬戶),即使用戶的所有信息一致也是如此。本文旨在分析具體原因以及尋求一種可實(shí)施的解決方案
相關(guān)鏈接:
- soanrqube :https://github.com/SonarSource/sonarqube
- soanr-auth-oidc : https://github.com/vaulttec/sonar-auth-oidc
sonar 插件實(shí)現(xiàn)分析
以 sonar-auth-oidc 為例,實(shí)現(xiàn)一個(gè) soanr 的插件,需要如下步驟:
1、實(shí)現(xiàn)對(duì)應(yīng)的插件接口
soanr 將可以擴(kuò)展的通用的功能抽象定義放到了 sonar-plugin-api 模塊,實(shí)現(xiàn)插件首先需要依賴這個(gè)模塊,然后需要實(shí)現(xiàn)什么功能,找到對(duì)應(yīng)的接口定義,以 sonar-auth-oidc 為例,需要實(shí)現(xiàn) OAuth2IdentityProvider 接口。
@ServerSide public class OidcIdentityProvider implements OAuth2IdentityProvider { private static final Logger LOGGER = Loggers.get(OidcIdentityProvider.class); public static final String KEY = "oidc"; private final OidcConfiguration config; private final OidcClient client; private final UserIdentityFactory userIdentityFactory; public OidcIdentityProvider(OidcConfiguration config, OidcClient client, UserIdentityFactory userIdentityFactory) { this.config = config; this.client = client; this.userIdentityFactory = userIdentityFactory; } //省略非關(guān)鍵邏輯 @Override public void init(InitContext context) { LOGGER.trace("Starting authentication workflow"); if (!isEnabled()) { throw new IllegalStateException("OpenID Connect authentication is disabled"); } String state = context.generateCsrfState(); AuthenticationRequest authenticationRequest = client.getAuthenticationRequest(context.getCallbackUrl(), state); LOGGER.trace("Redirecting to authentication endpoint"); context.redirectTo(authenticationRequest.toURI().toString()); } @Override public void callback(CallbackContext context) { LOGGER.trace("Handling authentication response"); context.verifyCsrfState(); AuthorizationCode authorizationCode = client.getAuthorizationCode(context.getRequest()); UserInfo userInfo = client.getUserInfo(authorizationCode, context.getCallbackUrl()); UserIdentity userIdentity = userIdentityFactory.create(userInfo); LOGGER.debug("Authenticating user '{}' with groups {}", userIdentity.getProviderLogin(), userIdentity.getGroups()); context.authenticate(userIdentity); LOGGER.trace("Redirecting to requested page"); context.redirectToRequestedPage(); } }
2、實(shí)現(xiàn) plugin 接口,聲明擴(kuò)展類
public class AuthOidcPlugin implements Plugin { @Override public void define(Context context) { context.addExtensions(OidcConfiguration.class, OidcClient.class, OidcIdentityProvider.class, UserIdentityFactory.class); context.addExtensions(OidcConfiguration.definitions()); } }
3、使用插件打包工具,打包插件
<plugin> <groupId>org.sonarsource.sonar-packaging-maven-plugin</groupId> <artifactId>sonar-packaging-maven-plugin</artifactId> <version>1.18.0.372</version> <extensions>true</extensions> <configuration> <pluginClass>org.vaulttec.sonarqube.auth.oidc.AuthOidcPlugin</pluginClass> </configuration> </plugin>
插件工作原理淺析
插件完成后,最終會(huì)打成 jar 包,在應(yīng)用市場(chǎng)安裝插件,相當(dāng)于下載了一個(gè) jar 放到了 soanrqube 安裝目錄的 ./extensions/plugins 路徑下。上面的打包工具會(huì)給 jar 包清單 META-INF/MANIFEST.MF 里加上
Plugin-Class: org.vaulttec.sonarqube.auth.oidc.AuthOidcPlugin 。
soanrqube 啟動(dòng)時(shí),插件掃描器會(huì)掃到這個(gè) jar ,解析拿到 plugin-class 并加載插件。
sonar-auth-oidc 實(shí)現(xiàn)邏輯分析
從sonar 插件實(shí)現(xiàn)分析了解到,sonar-auth-oidc 實(shí)現(xiàn) Oauth 認(rèn)證的關(guān)鍵邏輯在 OidcIdentityProvider 類里,關(guān)鍵代碼如下:
init 方法
集成 oidc 插件后,訪問(wèn) soanr 會(huì)顯示如下登錄入口
該登錄按鈕的鏈接為:http://172.26.202.128:9000/sessions/init/oidc?return_to=%2F ,點(diǎn)擊這個(gè)鏈接,就會(huì)觸發(fā)插件里 init 方法,init 方法里拼裝了 oidc 授權(quán)服務(wù)器的接口地址并重定向到這地址,等待用戶授權(quán)
callback 方法
用戶在授權(quán)服務(wù)器授權(quán)頁(yè)面授權(quán)完成后,授權(quán)服務(wù)器會(huì)回調(diào) soanr 服務(wù),最終觸發(fā) callback 函數(shù)。在 callback 函數(shù)里,首先從 request 里拿到了 authorizationCode ,然后使用 code 請(qǐng)求 oidc 授權(quán)服務(wù)器,拿到了 UserInfo,使用 UserInfo 里的信息,組裝了 soanr 內(nèi)置的認(rèn)證類 UserIdentity 。然后調(diào)用了 authenticate 完成認(rèn)證,如果認(rèn)證成功,沒(méi)有異常,則重定向到了登錄成功的頁(yè)面。看到這里,最關(guān)鍵的地方是 UserIdentity ,前面一大堆邏輯,最終就為了組裝出這個(gè)實(shí)體,這個(gè)類定義如下:
sonar-auth-oidc 插件里有用的信息,就這些了,關(guān)鍵的是 UserIdentity ,接下來(lái)需要從 sonarqube 里找到 CallBackContext 的實(shí)現(xiàn)類,看 authenticate() 方法里是怎么處理 UserIdentity 的。
soanrqube 的 auth 模塊分析
soanr 的web 登錄認(rèn)證功能在 sonar-webserver-auth 模塊,上文在 oidc 插件里出現(xiàn)的 InitContext 、 CallBackContxt 均在 OAuth2ContextFactory 類里被初始化,關(guān)鍵邏輯如下:
authenticate 過(guò)程
- 1、執(zhí)行用戶注冊(cè)邏輯(內(nèi)部控制了是注冊(cè)還是更新)
- 2、生成登錄用的 jwt 串
- 3、設(shè)置本地用戶登錄的 session
register 過(guò)程
1、通過(guò) externalId 和 IdentityProvider 查詢用戶,如果為空,則通過(guò) externalLogin 和 IdentityProvider 查詢用戶
2、如果 user 不存在或者已禁用,則創(chuàng)建一個(gè)新的用戶,否則注冊(cè)一個(gè)已存在的用戶(更新用戶信息)
關(guān)鍵點(diǎn)開(kāi)始凸顯出來(lái)了,關(guān)鍵邏輯在這里,通過(guò) externalId 和 IdentityProvider、externalLogin 等條件沒(méi)有查詢到用戶信息,所以才創(chuàng)建了新的用戶,接下來(lái)看下數(shù)據(jù)庫(kù)里 User 存儲(chǔ)了哪些信息。
User 表數(shù)據(jù)分析
本地 sonar 嘗試對(duì)接過(guò) ldap 、gitlab 、oidc ,所以一個(gè)賬戶有三條用戶數(shù)據(jù),其中 ldap 賬戶的 identityProvider 被標(biāo)記為 sonarqube 了,而使用 OIDC 登錄被標(biāo)記為 oidc(插件的 key),所以才導(dǎo)致通過(guò) externalId 和 IdentityProvider、externalLogin 等信息都查詢不到用戶信息
解決方案
經(jīng)過(guò)分析后,知道了 oidc 和 ldap 賬戶互斥的根本原因?yàn)椋煌?identityProvider 的用戶會(huì)被加上 provider_key 區(qū)分,導(dǎo)致同一個(gè)用戶,即使用戶信息一樣,從不同的 identityProvider 認(rèn)證也會(huì)生成不同的用戶。所以,可以從如下兩個(gè)方案出發(fā),解決問(wèn)題
方案一:重寫(xiě)插件,將 identityProvider 標(biāo)識(shí)和 LDAP 的同步。
- 優(yōu)點(diǎn):兼容 LDAP 和 identityProvider 賬戶,可以設(shè)計(jì)下兼容所以的 identityProvider
- 缺點(diǎn):實(shí)施比較困難,需要自己維護(hù)插件或者尋求官方倉(cāng)庫(kù)合并 pr
方案二:修改數(shù)據(jù)庫(kù)中存量 LDAP 用戶的 identityProvider 的標(biāo)識(shí)為 oidc。
- 優(yōu)點(diǎn):改動(dòng)較簡(jiǎn)單
- 缺點(diǎn):LDAP 和 OIDC 不兼容,集成 OIDC 后,只能被動(dòng)的關(guān)閉 LDAP,而原 LDAP 的輸入框和本地用戶密碼框是一起的,沒(méi)法移除,對(duì)用戶使用習(xí)慣會(huì)有影響
以上就是原理分析SonarQube中IdentityProvider賬戶互斥現(xiàn)象的詳細(xì)內(nèi)容,更多關(guān)于分析SonarQube中IdentityProvider賬戶互斥的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
SpringCloud-Alibaba-Sentinel服務(wù)降級(jí),熱點(diǎn)限流,服務(wù)熔斷
這篇文章主要介紹了SpringCloud-Alibaba-Sentinel服務(wù)降級(jí),熱點(diǎn)限流,服務(wù)熔斷,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12SpringBoot之那些注入不了的Spring占位符(${}表達(dá)式)問(wèn)題
這篇文章主要介紹了SpringBoot之那些注入不了的Spring占位符(${}表達(dá)式)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04SpringBoot2開(kāi)啟Actuator端點(diǎn)監(jiān)控的方法
這篇文章主要介紹了SpringBoot2開(kāi)啟Actuator端點(diǎn)監(jiān)控的相關(guān)資料,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06詳解關(guān)于Windows10 Java環(huán)境變量配置問(wèn)題的解決辦法
這篇文章主要介紹了關(guān)于Windows10 Java環(huán)境變量配置問(wèn)題的解決辦法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-03-03Java+Swing實(shí)現(xiàn)中國(guó)象棋游戲
這篇文章將通過(guò)Java+Swing實(shí)現(xiàn)經(jīng)典的中國(guó)象棋游戲。文中可以實(shí)現(xiàn)開(kāi)始游戲,悔棋,退出等功能。感興趣的小伙伴可以跟隨小編一起動(dòng)手試一試2022-02-02