主人看到navicat和mysql在那嘻嘻哈哈,眉來眼去的,好不快樂,忽然也想自己寫個程序,直接去訪問Mysql,雖然現在已經有很多現成的中間件可以直接拿來用了,程序只要負責寫sql語句就行了,但是主人想要自己通過mysql協議直接和mysql通訊,一窺究竟。於是主人找到Mysql說:親愛的mysql,我以前和你交流總要通過第三方的驅動在中間傳話,總感覺我們之間還有一個隔閡,有些話也不方便說,我現在有些心裏話想直接和你交流。。。你說行嗎?
mysql說:當然行啊,mysql受寵若驚,要和我打交道有多種方法比如:TCP/IP,TLS/SSL,Unix Sockets,Shared Memory,Named pipes等,那我們就用TCP/IP的方吧。用tcp協議就繞不開三次握手連接和四次握手斷開,所以呢你和我連接的第一件事就是三次握手連接。
主人尷尬的笑了笑,tcp的三次握手聽到聽說過很多次,但是從沒有真正的理解。。。
mysql從身後丟過來一個便籤:這裏有篇文章可以參考下:https://www.cnblogs.com/zhanyd/p/9877762.html
主人謝道,還是你體貼,剛開始navicat和你連接的時候,我是輸入了主機地址,用戶名,密碼的,你們之間是怎麼驗證的呢?
mysql說:好問題,所有的客戶端和我連接首先都要先經過我的認證,我和客戶端一次正常的交互過程如下:
1. 三次握手建立 TCP 連接。 2. 建立 MySQL 連接,也就是認證階段。 服務端 -> 客戶端:發送握手初始化包 (Handshake Initialization Packet)。 客戶端 -> 服務端:發送驗證包 (Client Authentication Packet)。 服務端 -> 客戶端:認證結果消息。 3. 認證通過之後,客戶端開始與服務端之間交互,也就是命令執行階段。 客戶端 -> 服務端:發送命令包 (Command Packet)。 服務端 -> 客戶端:發送迴應包 (OK Packet, or Error Packet, or Result Set Packet)。 4. 斷開 MySQL 連接。 客戶端 -> 服務器:發送退出命令包。 5. 四次握手斷開 TCP 連接。
我專門搞了個認證報文格式,我會按照以下的格式給客戶端發送數據,然後客戶端要根據這裏面的內容給我返回驗證包,然後我判斷是否有權限登錄:
官方的文檔是這樣子滴:
感覺不直觀,在網上找到一個更直觀的圖:
具體解釋如下:
-
protocol_version (1) --
0x0a
protocol_version第一個字節表示協議版本號
-
server_version (string.NUL) -- human-readable server version
服務器版本號,字符串遇到Null結束
-
connection_id (4) -- connection id
服務器線程id
-
auth_plugin_data_part_1 (string.fix_len) -- [len=8] first 8 bytes of the auth-plugin data
第一部分8個字節的挑戰隨機數,後面還有第二部分
-
filler_1 (1) --
0x00
填充位
0x00
-
capability_flag_1 (2) -- lower 2 bytes of the
Protocol::CapabilityFlags
(optional)服務器權能標誌(低位2個字節)
-
character_set (1) -- default server character-set, only the lower 8-bits
Protocol::CharacterSet
(optional)This “character set” value is really a collation ID but implies the character set; see the
Protocol::CharacterSet
description.字符編碼
-
status_flags (2) --
Protocol::StatusFlags
(optional)服務器狀態
-
capability_flags_2 (2) -- upper 2 bytes of the
Protocol::CapabilityFlags
服務器權能標誌(高位2個字節)
-
auth_plugin_data_len (1) -- length of the combined auth_plugin_data, if auth_plugin_data_len is > 0
挑戰隨機數的長度
-
string[10] reserved (all [00])
10個字節的保留位,都是00
-
auth_plugin_data_part_2
挑戰隨機數的第二部分,通常是12字節
-
挑戰隨機數結束標誌00
-
auth_plugin_name (string.NUL) -- name of the auth_method that the auth_plugin_data belongs to
認證插件的名稱,null結尾(這部分上面的圖表裏沒有加進去)
主人聽完後,躍躍欲試,很想驗證下Mysql說的是不是真的,於是他找到了密友Wiresshark,讓他監聽下navicat和mysql之間的認證包,Wiresshark很快就完成了任務,把結果呈上來了:
具體先看服務器發送過來的第一個包:
主人一看,居然和mysql說的一模一樣,好神奇。。。
mysql笑道:那當然,我還能騙你不成。我發給客戶端收到後,客戶端就要返回認證包給我驗證啦,是驢是馬我一眼就能認出來了哦,客戶端返回給我要遵循以下的格式:
Fields
-
capability_flags (4) -- capability flags of the client as defined in
Protocol::CapabilityFlags
客戶端權能標誌
-
max_packet_size (4) -- max size of a command packet that the client wants to send to the server
報文的最大字節數
-
character_set (1) -- connection's default character set as defined in
Protocol::CharacterSet
.字符集編碼
-
username (string.fix_len) -- name of the SQL account which client wants to log in -- this string should be interpreted using the character set indicated by
character set
field.用戶名
-
auth-response (string.NUL) -- opaque authentication response data generated by Authentication Method indicated by the
plugin name
field用戶認證信息,即密碼明文和挑戰隨機數加密後的token
-
database (string.NUL) -- initail database for the connection -- this string should be interpreted using the character set indicated by
character set
field.數據庫名稱
-
auth plugin name (string.NUL) -- the Authentication Method used by the client to generate
auth-response
value in this packet. This is an UTF-8 string.認證方法
主人抓包的結果:
mysql收到了主人發過來的認證包:主人,經過驗證用戶名密碼都是正確的,可以登錄了,我要返回一個ok報文,告訴你操作成功了哦,報文結構如下:
-
header:
OK:
header
= 0 and length of packet > 7header=0並且報文長度>7表示當前是ok報文
EOF:
header
= 0xfe and length of packet < 9header=0xfe並且報文長度<9表示當前是eof報文
主人抓包的結果:
header = 0,表示這個是個ok報文,status_flags(server status)= 02表名設置自動提交成功。
主人很高興:這是不是說明,我和你的連接成功了呀?
mysql:恭喜你連接成功了,我們走出了第一步,接下來你就可以發送命令讓我執行了喲。