MySQL协议是一个有状态的协议。
Connection Phase
。Command Phase
,(TCP)连接终止命令阶段结束。Replication Protocol
。Connection Phase
在连接阶段执行一下任务:
从客户端连接服务器开始,服务器可以发送一个ERR
数据包来结束握手或者发生一个初始Handshake
数据包,客户端收到后会回复一个Handshake
响应包。在此阶段,客户端可以请求SSL连接,在这种情况下,必须在客户端发送身份验证响应前建立SSL通信通道。
如果服务器将
ERR
数据包作为第一个数据包发送,那么这个发送行为是在客户端和服务器协商任何功能之前发生的。因此,ERR
数据包将不包含SQL状态。
初次握手后,服务器将通知客户端有关身份验证的方法(除非在握手期间已经确定该方法),并且身份验证交换继续进行,直到服务器发送OK_Packet
接受连接或者发送ERR_Packet
拒绝连接。
初次握手从服务器发送Protocol::Handshake
数据包开始。在这之后,客户端可以选择使用Protocol::SSLRequest
数据包请求建立SSL连接,然后客户端发送Protocol::HandshakeResponse
数据包。
Protocol::Handshake
Protocol::HandshakeResponse
Protocol::Handshake
Protocol::SSLRequest
Protocol::HandshakeResponse
为了允许旧客户端连接到新服务器,Protocol::Handshake
包含MySQL服务器版本即服务器的功能标志。
客户端在Protocol::HandshakeResponse
中只声明与服务器相同的功能。
然后使用如下参数达成一致:
用于身份验证的方法将与用户帐户绑定,并存储在mysql.user
表的plugin
列中。客户端在Protocol::HandshakeResponse
数据包中发送将要登录的用户帐户。这样,服务器就能查找mysql.user
表并找到要使用的身份验证方法。
为节省通信成本,服务器和客户端在发送初始Handshake
数据包时就对要使用的身份验证方法进行了推测。
服务器使用其默认身份验证方法default_auth_plugin
生成初始身份验证数据的有效负载,并将其与方法名放在Protocol::Handshake
中一起发给客户端。
客户端在Protocol::HandshakeResponse
中包含对服务器发送的身份验证数据的答复。
当在Protocol::HandshakeResponse
中包括身份验证回复时,客户端没有义务使用与Protocol::Handshake
数据包中服务器所使用的身份验证方法相同的身份验证方法。客户端使用的身份验证方法的名称存储在响应数据包中。如果客户端或服务器在初始握手中包换的推测身份验证方法不正确,则服务器会使用Protocol::AuthSwitchRequest
通知客户端应使用哪种身份验证方法。
在MySQL 4.0之前,MySQL协议仅支持Old Password Authentication
。在MySQL 4.1中,添加了Native Authentication
方法,而在MySQL 5.5中,可以通过身份验证插件实现任意身份验证方法。
如果客户端或服务器不支持插件式身份验证(即未设置CLIENT_PLUGIN_AUTH
功能标志),则从客户端和服务器功能继承使用的身份验证方法,如下所示:
CLIENT_PROTOCOL_41
或CLIENT_SECURE_CONNECTION
,则使用的方法是Old Password Authentication
。CLIENT_PROTOCOL_41
和CLIENT_SECURE_CONNECTION
,但未设置CLIENT_PLUGIN_AUTH
,则使用的方法是Native Authentication
。假设客户端要通过用户帐户U
登录,并且该用户帐户已定义为使用身份验证方法server_method
。在以下情况下使用快速身份验证路径:
server_method
生成身份验证数据后放入Protocol::Handshake
数据包中。Protocol::HandshakeResponse
中声明使用client_authentication_method
,该方法与服务器使用的server_method
兼容。这样在握手期间就已经开始了第一轮身份验证。然后,根据身份验证方法server_method
,进一步交换身份验证,直到服务器接受或拒绝身份验证为止。
成功执行快速身份验证路径的步骤如下:
Protocol::Handshake
Protocol::HandshakeResponse
OK_Packet
服务器在步骤4中发送一个Protocol::AuthMoreData
数据包,其前缀为0x01
,来区别于ERR_Packet
和OK_Packet
。
注意:许多身份验证方法(包括
mysql_native_password
方法)由单个请求-响应交换组成。因此,在步骤4中不会交换任何额外的数据包,并且服务器在接收到Protocol::HandshakeResponse
数据包后(如果身份验证成功)就直接发送OK_Packet
。
它与身份验证成功完全一样,只是在用户身份验证失败的时候,服务器将以ERR_Packet
而不是OK_Packet
进行回复。
假设客户端要以用户U
身份登录,并且该用户帐户使用身份验证方法M
。如果:
Protocol::Handshake
数据包中的身份验证有效负载的默认方法与M
不同Protocol::HandshakeResponse
数据包中的方法与M
不兼容则说明身份验证方法不匹配,必须使用正确的身份验证方法重新启动身份验证交换过程。
注意:
- 即使客户端和服务器在初始握手中使用了兼容的身份验证方法,也可能发生不匹配,因为,服务器使用的方法与用户帐户所需的方法不同。
- 在4.1-5.7版本的服务器和客户端中,默认身份验证方法是
Native Authentication
。- 在8.0版本的服务器和客户端中,默认的身份验证方法是
Caching_sha2_password information
。- 客户端和服务器可以通过
--default-auth
选项更改其默认身份验证方法。- 对客户端来说,查看在
Protocol::Handshake
数据包中声明的服务器的默认身份验证方法,并从中推断出身份验证方法,比在生成Protocol::HandshakeResponse
数据包时直接使用客户端默认身份验证方法更好。但是,由于服务器和客户端之间存在一对多的身份验证方法插件,而且,客户端通常都不知道这种映射关系,因此这在mysql
客户端库中未实现。
如果发生身份验证方法不匹配,服务器将向客户端发送Protocol::AuthSwitchRequest
数据包,其中包含服务器要使用的身份验证方法的名称以及使用新方法重新生成的第一个身份验证有效负载。客户端应切换到服务器请求的身份验证方法,并按照该方法的指示继续进行交换。
如果客户端不知道所请求的方法,则应断开连接。
Protocol::Handshake
Protocol::HandshakeResponse
Protocol::AuthSwitchRequest
来告知客户端需要更换一个新的验证方法OK_Packet
或者ERR_Packet
表示拒绝如果服务器发现客户端功能不足以完成身份验证,则服务器将使用ERR_Packet
拒绝。在以下情况下可能会发生这种情况:
CLIENT_PLUGIN_AUTH
标志)的客户端连接到使用与Native Authentication
方法不同的帐户CLIENT_SECURE_CONNECTION
标志)的客户端尝试建立连接Protocol::Handshake
数据包中生成身份验证数据)与Native Authentication
不兼容,并且客户端不支持插件式身份验证(未设置CLIENT_PLUGIN_AUTH
标志)在以上任何一种情况下,身份验证阶段都将如下所示:
Protocol::Handshake
Protocol::HandshakeResponse
ERR_Packet
并关闭连接即便客户端支持外部(插件式)身份验证(设置了CLIENT_PLUGIN_AUTH
标志),也可能不知道Protocol::AuthSwitchRequest
数据包中声明的新的身份验证方法。在这种情况下,客户端只需断开连接即可。
Protocol::Handshake
Protocol::HandshakeResponse
Protocol::AuthSwitchRequest
来告知客户端需要更换一个新的验证方法Non-CLIENT_PLUGIN_AUTH
客户端注意:这只会在8.0版本之前的服务器上发生。 8.0版本开始移除了
Old Password Authentication
。
服务器将向未设置CLIENT_PLUGIN_AUTH
标志的客户端请求更改身份验证方法的唯一可能是满足一下条件:
Protocol::HandshakeResponse
数据包中使用Old Password Authentication
CLIENT_SECURE_CONNECTION
)Native Authentication
在这种情况下,服务器发送Protocol::OldAuthSwitchRequest
数据包,其中不包含新的验证方法的名称,因为它被隐式假定为Native Authentication
,并且其中不包含身份验证数据。 客户端响应Protocol::HandshakeResponse320
。要生成密码哈希,客户端必须重用服务器在Protocol::Handshake
中发送的随机字节。
COM_CHANGE_USER
指令后的验证在命令阶段,客户端可以发送COM_CHANGE_USER
命令,该命令将通过完全身份验证握手来触发对新帐户的身份验证。
与连接阶段类似,服务器可以使用ERR_Packet
或OK_Packet
来响应快速身份验证路径,或者发送Protocol::AuthSwitchRequest
数据包进行响应,在该数据包中包含要用于新帐户的身份验证方法以及客户端要使用的第一个身份验证数据的有效负载 。根据新帐户的身份验证方法的定义,执行进一步的握手。最终,服务器将接受新帐户并响应OK_Packet
,或者发生ERR_Packet
来拒绝这种比变更并断开连接。
COM_CHANGE_USER
数据包Protocol::AuthSwitchRequest
,来使用正确的身份验证方法启动身份验证握手OK_Packet
响应并返回命令阶段或以ERR_Packet
相应并关闭连接COM_CHANGE_USER
和Non-CLIENT_PLUGIN_AUTH
客户端不支持可插件式身份验证的客户端可以为使用Native Authentication
或Old Password Authentication
验证的账户发送COM_CHANGE_USER
命令。在这种情况下,假定服务器已经发送了身份验证质询(与客户端第一次连接时发送的身份质询相同),并且客户端对该质询的答复(即新密码的哈希)应在发送auth_response
中包含 COM_CHANGE_USER
字段。
COM_CHANGE_USER
数据包,其中包含Native Authentication
(4.1版后的客户端)或Old Password Authentication
(4.1版之前的客户端)方法的身份验证响应(即密码的哈希值)OK_Packet
响应并返回到命令阶段,或者以ERR_Packet
返回并关闭连接与正常连接期间一样,不支持插件式身份验证的4.1版客户端也有可能连接到使用Old Password Authentication
的帐户,在这种情况下,服务器将发送Protocol::OldAuthSwitchRequest
并期望客户端以Protocol::HandshakeResponse320
来响应。
COM_CHANGE_USER
数据包来响应Native Authentication
Protocol::OldAuthSwitchRequest
(0xFE字节)Old Password Authentication
所需的形式发送响应OK_Packet
响应并返回到命令阶段或以ERR_Packet
返回并断开连接