Doris開發手記1:解決蛋疼的MySQL 8.0鏈接問題

筆者做爲Apache Doris的開發者,平時感受相關Doris的文章寫的不多。主要是不少時候不知道應該去記錄一些怎麼樣的問題,感受寫的很差就會很慌張。新的一年,但願記錄本身在Doris開發過程之中所遇到一些有意思的事情。(只但願能堅持下來,別打臉~~
言歸正傳,回到本篇想聊的問一個問題,筆者在開發ODBC of Doris的工做之中,發現經過MySQL 8.0的Driver鏈接Doris老是提示密碼驗證失敗。可是因爲開發工做繁忙,一直沒有騰出手解決這個問題。最近從新抽時間梳理了一下這個問題,這個問題自己不難解決,可是解決問題的思路我以爲值得與你們分享,獻醜了啊,各位~~html

1.老革命遇到新問題

使用MySQL 8.0的客戶端鏈接Doris時,若是不添加以下參數--default-auth=mysql_native_password的話,總會出現以下提示的密碼認證錯誤:mysql

ERROR 1045 (28000): Access denied for user 'default_cluster:test' (using password: YES)

一樣的密碼認證問題也會同時出如今了使用ODBC的MySQL 8.0以上的Driver鏈接Doris時。更使人蛋疼的是,使用ODBC連接時並無法調用上面的參數進行問題的規避。這會帶來兩個問題:git

    1. Doris自己的ODBC外表沒法經過MySQL 8.0以上的Driver鏈接Doris
  • 2.許多流行的BI分析工具如Tableau等:也沒法經過ODBC的方式鏈接Doris

以前經過5.x的客戶端和Driver能夠順利的鏈接Doris,而如今真是老革命趕上新問題了。github

默認的密碼認證插件的變動

其實新問題的引入很簡單,就是MySQL的客戶端從8.0的版本開始,將原先客戶端的默認的密碼認證插件由mysql_native_password改成了caching_sha2_password,兩種密碼認證方式不一樣。而Doris當前只支持mysql_native_password的密碼認證插件,因此就致使了鏈接時密碼認證失敗了。而關於密碼認證插件的變動,更爲詳細的內容,能夠參考MySQL的官方文檔sql

2.問題的分析與梳理

好的,確認了問題,就開始研究解決方案。從直覺上說,Doris支持新的caching_sha2_password密碼認證插件確定是最直接的解決思路。這種作法確定是一勞永逸的解決問題的,可是這就得重構整個Doris的密碼管理系統,開發和支持起來的代價實在是有些太大了。數據庫

那既然咱們否認了這種方式,就得另外想辦法解決了。首先,使用MySQL 8.0的客戶端鏈接Doris時,添加以下參數--default-auth=mysql_native_password即可以認證成功。 因此問題就回到了如何讓ODBC的鏈接可以支持上述參數,筆者經歷了下面的分析歷程:apache

2.1 ODBC鏈接文檔

ODBC是經過鏈接串的方式傳參給MySQL的鏈接Driver的,若是可以像使用MySQL客戶端的方式添加參數即可以解決,那麼天然無代碼的Coding是成本最低的解決方案。安全

筆者首先嚐試查看了MySQL官方的ODBC鏈接參數文檔,遺憾的是,並無找到ODBC關於認證方式的任何內容,這也就意爲着:此路不通服務器

2.2 新舊版本的兼容性

既然MySQL從8.0開始切換了默認的密碼認證插件,那麼新的客戶端是否能夠鏈接老的MySQL服務器呢?MySQL自己是如何解決新老客戶端的兼容問題的呢?網絡

因而筆者嘗試使用MySQL 8.0的客戶端鏈接了MySQL的5.x的服務器,發現了下面的線索:新客戶端並不須要像鏈接Doris同樣,修改默認的密碼認證插件。那也就意味着,MySQL的客戶端和服務器能夠在鏈接過程之中經過某種方式交換確認一種服務器支持的密碼認證方式。

既然如此,筆者開始了Google之旅,可是並無搜索到什麼有價值的信息。沒辦法,源碼面前,了無祕密。因而筆者決定嘗試閱讀一下MySQL Client端的代碼,看看是否能發現上述的交互邏輯。

通過一番"痛苦"的源碼搜索和閱讀,筆者在找到了以下的註釋,完整的闡述了MySQL的客戶端與服務器的鏈接過程:

1. The client connects to the server
  2. The server sends @ref page_protocol_connection_phase_packets_protocol_handshake
  3. The client respons with
  @ref page_protocol_connection_phase_packets_protocol_handshake_response
  4. The server sends the
  @ref page_protocol_connection_phase_packets_protocol_auth_switch_request to tell
  the client that it needs to switch to a new authentication method.
  5. Client and server possibly exchange further packets as required by the server
  authentication method for the user account the client is trying to authenticate
  against.
  6. The server responds with an @ref page_protocol_basic_ok_packet or rejects
    with @ref page_protocol_basic_err_packet

把上述的註釋讀懂以後,筆者又回頭查閱了一下Doris之中處理MySQL客戶端鏈接的代碼。總算是整明白了爲啥新的客戶端鏈接Doris會失敗了,這個是新客戶端鏈接Doris的流程:

Drois  ->: Authentication Plugin: mysql_native_password
Client <-: Client Auth Plugin: caching_sha2_password
Doris  ->: MySQL Error 2012 (HY000): Password check failed.

而新客戶端鏈接老的MySQL的流程以下:

Mysql  ->: Authentication Plugin: mysql_native_password
Client <-: Client Auth Plugin: caching_sha2_password
Mysql  ->: Auth Switch Request: Auth Method Name: mysql_native_password
Client <-: Auth Switch Response
Mysql  ->: OK

MySQL的服務器支持了Auth Switch Request的網絡請求來告知客戶端本身支持的認證的密碼插件,而客戶端會進行密碼插件的支持檢查,而客戶端則將密碼插件加密的結果返回。

3.開發起來,解決問題

經過上一小節的分析,問題已經水落石出了。接下來就是如何在Doris上支持Auth Switch Request的網絡請求。

3.1 確認二進制結構

因此這裏就須要研究這兩個Auth Switch RequestAuth Switch Response的二進制包是如何組成的。這裏再次藉助官方文檔,確認了兩個包的組成:

AuthSwitchRequest

AuthSwitchResponse

3.2 代碼開發

其實到這裏的工做已經很簡單了,直接上筆者修改Doris的代碼吧:

if (!handshakePacket.checkAuthPluginSameAsDoris(authPacket.getPluginName())) {
            // 1. clear the serializer
            serializer.reset();
            // 2. build the auth switch request and send to the client
            handshakePacket.buildAuthSwitchRequest(serializer);
            channel.sendAndFlush(serializer.toByteBuffer());
            // Server receive auth switch response packet from client.
            ByteBuffer authSwitchResponse = channel.fetchOnePacket();
            if (authSwitchResponse == null) {
                // receive response failed.
                return false;
            }
            // 3. the client use default password plugin of Doris to dispose
            // password
            authPacket.setAuthResponse(readEofString(authSwitchResponse));
        }

就是進行了密碼認證插件的校驗,若是不Match Doris默認的密碼認證插件的話,則構造AuthSwitchRequest發送給客戶端。(筆者這裏只列出了部分代碼,完整的代碼修改請參考以下的pr .

Coding完成以後,編譯部署,進行測試,問題解決,提出issue,把解決問題的代碼貢獻給Doris的官方代碼倉庫提pr。完結撒花~~~~

4.小結

Bingo! 到此爲止,問題順利解決了,但願經過和你們分享一個問題的解決流程,幫助你們梳理數據庫開發之中的解決問題的思路。咱們有着最大的三個幫手

  • 搜索引擎
  • 官方文檔
  • 源代碼

而若是你遇到的是Doris的問題,那你就有第四個幫手了:百度Doris團隊。(加星重點)

固然,這裏也留下一個TODO的問題:支持MySQL 8.0默認的caching_sha2_password的認證方式。相較原先的mysql_native_password的認證方式,它有必定的安全性優點,可是這樣可能須要重構整個Doris的密碼體系了。

最後,也但願你們多多支持Apache Doris,多多給Doris貢獻代碼,感恩~~

5.參考資料

MySQL官方文檔
MySQL源代碼
Apache Doris源代碼

相關文章
相關標籤/搜索