主人看到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:恭喜你连接成功了,我们走出了第一步,接下来你就可以发送命令让我执行了哟。
原文地址:https://www.cnblogs.com/zhanyd/p/9895241.html