当您尝试连接到 MySQL 服务器时,服务器会根据您的身份以及您是否可以通过提供正确的密码来验证其身份来接受或拒绝连接。如果不是,服务器将完全拒绝您的访问。否则,服务器接受连接,然后进入阶段 2 等待请求。
服务器使用表中的列执行身份和凭据检查user
,仅当满足以下条件时才接受连接:
客户端主机名和用户名与 某些表行中的
Host
和User
列相匹配。有关管理允许和 值user
的规则,请参阅第 6.2.4 节,“指定帐户名称”。Host
User
客户端提供行中指定的密码,如
Password
列所示。
您的身份基于两条信息:
您的 MySQL 用户名。
您连接的客户端主机。
如果User
列值不为空,则传入连接中的用户名必须完全匹配。如果该
User
值为空,则匹配任何用户名。如果user
与传入连接匹配的表行具有空白用户名,则该用户被认为是没有名称的匿名用户,而不是具有客户端实际指定名称的用户。这意味着在连接期间(即第 2 阶段),所有进一步的访问检查都将使用空白用户名。
该Password
列可以为空。这不是通配符,并不意味着任何密码都匹配。这意味着用户必须在不指定密码的情况下进行连接。如果服务器使用插件对客户端进行身份验证,则插件实现的身份验证方法可能会也可能不会使用Password
列中的密码。在这种情况下,外部密码也可能用于对 MySQL 服务器进行身份验证。
Password
表列中
存储的非空密码值user
已加密。MySQL 不会将密码存储为任何人都可以看到的明文。相反,尝试连接的用户提供的密码是加密的(使用PASSWORD()
函数)。然后在连接过程中检查密码是否正确时使用加密后的密码。这是在没有通过连接传输加密密码的情况下完成的。请参阅第 6.2.1 节,“帐户用户名和密码”。
从 MySQL 的角度来看,加密后的密码才是
真正的密码,所以你不应该让任何人访问它。特别是,不要授予非管理员用户对
mysql
系统数据库中表的读取权限。
下表显示了表中的
User
和Host
值的
各种组合如何user
应用于传入连接。
User 价值 |
Host 价值 |
允许的连接 |
---|---|---|
'fred' |
'h1.example.net' |
fred , 连接自
h1.example.net |
'' |
'h1.example.net' |
任何用户,从h1.example.net |
'fred' |
'%' |
fred , 从任何主机连接 |
'' |
'%' |
任何用户,从任何主机连接 |
'fred' |
'%.example.net' |
fred , 从
example.net 域中的任何主机连接 |
'fred' |
'x.example.%' |
fred , 连接自
x.example.net ,
x.example.com ,
x.example.edu , 等等; 这可能没用 |
'fred' |
'198.51.100.177' |
fred , 从具有 IP 地址的主机连接
198.51.100.177 |
'fred' |
'198.51.100.%' |
fred 198.51.100 , 从C 类子网中的任何主机连接
|
'fred' |
'198.51.100.0/255.255.255.0' |
与前面的示例相同 |
传入连接的客户端主机名和用户名可能与
user
表中的多行匹配。前面的一组示例演示了这一点:显示的多个条目与来自h1.example.net
by的连接相匹配fred
。
当可能有多个匹配项时,服务器必须确定使用其中的哪一个。它按如下方式解决此问题:
每当服务器将
user
表读入内存时,它都会对行进行排序。当客户端尝试连接时,服务器会按排序顺序查看行。
服务器使用与客户端主机名和用户名匹配的第一行。
服务器使用排序规则,Host
首先对具有最具体值的行进行排序:
字面 IP 地址和主机名是最具体的。
文字 IP 地址的特异性不受其是否具有网络掩码的影响,因此
198.51.100.13
和198.51.100.0/255.255.255.0
被视为同等特异性。该模式
'%'
表示“任何主机”并且最不具体。空字符串
''
也表示“任何主机”,但排在 之后'%'
。
非 TCP(套接字文件、命名管道和共享内存)连接被视为本地连接,
localhost
如果有任何此类帐户,则与主机部分匹配,localhost
否则主机部分与通配符匹配(例如,, , local%
)
。
l%
%
具有相同Host
值的行按最具体的User
值排在最前面。空白
User
值表示“任何用户”并且最不具体,因此对于具有相同Host
值的行,非匿名用户排在匿名用户之前。
对于具有同样特定Host
和
User
值的行,顺序是不确定的。
要查看其工作原理,假设该user
表如下所示:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| % | root | ...
| % | jeffrey | ...
| localhost | root | ...
| localhost | | ...
+-----------+----------+-
当服务器将表读入内存时,它使用刚才描述的规则对行进行排序。排序后的结果是这样的:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| localhost | root | ...
| localhost | | ...
| % | jeffrey | ...
| % | root | ...
+-----------+----------+-
当客户端尝试连接时,服务器会查看已排序的行并使用找到的第一个匹配项。对于来自
localhost
by的连接jeffrey
,表中的两行匹配:具有
Host
和User
值
'localhost'
和的行''
,以及具有 和 值的'%'
行
'jeffrey'
。该'localhost'
行按排序顺序排在第一位,因此这是服务器使用的行。
这是另一个例子。假设该user
表如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| % | jeffrey | ...
| h1.example.net | | ...
+----------------+----------+-
排序后的表如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| h1.example.net | | ...
| % | jeffrey | ...
+----------------+----------+-
第一行匹配来自 的任何用户的连接
h1.example.net
,而第二行匹配jeffrey
来自任何主机的连接。
一种常见的误解是认为,对于给定的用户名,当服务器尝试为连接查找匹配项时,首先使用明确命名该用户的所有行。这不是真的。前面的示例说明了这一点,其中来自h1.example.net
by
的连接jeffrey
首先匹配的不是包含'jeffrey'
作为
User
列值的行,而是匹配没有用户名的行。结果,jeffrey
被认证为匿名用户,即使他在连接时指定了用户名。
如果您能够连接到服务器,但您的权限不是您所期望的,则您可能正在以其他帐户身份进行身份验证。要找出服务器用于对您进行身份验证的帐户,请使用该
CURRENT_USER()
功能。(请参阅
第 12.16 节,“信息函数”。)它返回一个格式的值,该
格式指示匹配
表行中的和
值。假设
连接并发出以下查询:
user_name
@host_name
User
Host
user
jeffrey
mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost |
+----------------+
此处显示的结果表明匹配的
user
表行具有空白
User
列值。换句话说,服务器被jeffrey
视为匿名用户。
诊断身份验证问题的另一种方法是打印出该user
表并手动对其进行排序,以查看第一个匹配的位置。