当您尝试连接到 MySQL 服务器时,服务器会根据以下条件接受或拒绝连接
您的身份以及您是否可以通过提供正确的凭据来验证您的身份。
您的帐户是否被锁定或解锁。
服务器首先检查凭据,然后检查帐户锁定状态。任一步骤失败都会导致服务器完全拒绝您访问。否则,服务器将接受连接,然后进入阶段 2 并等待请求。
服务器使用 user 表中的列执行身份和凭据检查,仅当满足以下条件时才接受连接
客户端主机名和用户名与某些
user表行中的Host和User列匹配。有关允许的Host和User值的规则,请参见 第 8.2.4 节,“指定帐户名称”。客户端提供行中指定的凭据(例如,密码),如
authentication_string列所示。凭据使用plugin列中命名的身份验证插件进行解释。该行表示帐户已解锁。锁定状态记录在
account_locked列中,该列必须值为'N'。帐户锁定可以使用CREATE USER或ALTER USER语句设置或更改。
您的身份基于两条信息
您的 MySQL 用户名。
您连接的客户端主机。
如果 User 列值非空,则传入连接中的用户名必须完全匹配。如果 User 值为空,则它将匹配任何用户名。如果与传入连接匹配的 user 表行具有空的用户名,则该用户被认为是匿名用户,没有名称,而不是具有客户端实际指定的名称的用户。这意味着空用户名将用于在连接持续时间内(即在阶段 2 期间)的所有后续访问检查。
authentication_string 列可以为空。这不是通配符,不表示任何密码都匹配。这意味着用户必须在不指定密码的情况下连接。由用于验证客户端的插件实现的身份验证方法可能会也可能不会使用 authentication_string 列中的密码。在这种情况下,外部密码也可能用于验证到 MySQL 服务器。
存储在 user 表的 authentication_string 列中的非空密码值已加密。MySQL 不会将密码以明文形式存储,以便任何人查看。相反,尝试连接的用户提供的密码会进行加密(使用帐户身份验证插件实现的密码哈希方法)。然后,在连接过程中检查密码是否正确时,将使用加密密码。这样做时,加密密码永远不会通过连接传输。请参见 第 8.2.1 节,“帐户用户名和密码”。
从 MySQL 服务器的角度来看,加密密码是真实密码,因此您永远不要将它提供给任何人。特别地,不要允许非管理用户读取 mysql 系统数据库中的表。
下表显示了 user 表中的 User 和 Host 值的各种组合如何应用于传入连接。
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 表中的多行匹配。上面的示例演示了这一点:显示的几个条目与 fred 从 h1.example.net 连接匹配。
当存在多个匹配项时,服务器必须确定使用哪一个。它按以下方式解决此问题
每当服务器将
user表读入内存时,它都会对行进行排序。当客户端尝试连接时,服务器会按排序顺序查看这些行。
服务器使用与客户端主机名和用户名匹配的第一行。
服务器使用排序规则,将具有最具体 Host 值的行排在最前面
文字 IP 地址和主机名是最具体的。
主机部分包含 IP 地址的帐户具有以下特异性顺序
主机部分以 IP 地址表示的帐户
CREATE USER 'user_name'@'127.0.0.1'; CREATE USER 'user_name'@'198.51.100.44';主机部分以 CIDR 表示法表示的 IP 地址的帐户
CREATE USER 'user_name'@'192.0.2.21/8'; CREATE USER 'user_name'@'198.51.100.44/16';主机部分以带子网掩码的 IP 地址表示的帐户
CREATE USER 'user_name'@'192.0.2.0/255.255.255.0'; CREATE USER 'user_name'@'198.51.0.0/255.255.0.0';
模式
'%'表示““任何主机””,并且最不具体。空字符串
''也表示““任何主机”,但在'%'之后排序。
非 TCP(套接字文件、命名管道和共享内存)连接被视为本地连接,如果存在任何此类帐户,则与 localhost 的主机部分匹配,否则与匹配 localhost 的通配符主机部分匹配(例如,local%、l%、%)。
将 '%' 视为等效于 localhost 已过时;您应该预期此行为将在 MySQL 的未来版本中移除。
具有相同 Host 值的行按最具体的 User 值排序。空白 User 值表示““任何用户””,并且最不具体,因此对于具有相同 Host 值的行,非匿名用户在匿名用户之前排序。
对于具有相同特异性 Host 和 User 值的行,顺序是不确定的。
要了解它的工作原理,假设 user 表如下所示
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| % | root | ...
| % | jeffrey | ...
| localhost | root | ...
| localhost | | ...
+-----------+----------+-当服务器将表读入内存时,它会使用刚刚描述的规则对行进行排序。排序后的结果如下所示
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| localhost | root | ...
| localhost | | ...
| % | jeffrey | ...
| % | root | ...
+-----------+----------+-当客户端尝试连接时,服务器会查看排序后的行,并使用找到的第一个匹配项。对于 jeffrey 从 localhost 连接,表中的两行匹配: Host 和 User 值为 'localhost' 和 '' 的一行,以及值为 '%' 和 'jeffrey' 的一行。在排序顺序中,'localhost' 行排在第一位,因此服务器使用的是该行。
以下是一个示例。假设 user 表如下所示
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| % | jeffrey | ...
| h1.example.net | | ...
+----------------+----------+-排序后的表如下所示
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| h1.example.net | | ...
| % | jeffrey | ...
+----------------+----------+-第一行与任何用户从 h1.example.net 连接匹配,而第二行与 jeffrey 从任何主机连接匹配。
很多人误以为,对于给定的用户名,所有明确命名该用户的行在服务器尝试查找连接匹配项时首先使用。这并不正确。前面的示例说明了这一点,其中 jeffrey 从 h1.example.net 连接的匹配项不是包含 'jeffrey' 作为 User 列值的行,而是没有用户名的行。因此,即使 jeffrey 在连接时指定了用户名,他也会被验证为匿名用户。
如果您能够连接到服务器,但您的权限不是您预期的,那么您可能正在被验证为其他帐户。要找出服务器用来验证您的帐户,请使用 CURRENT_USER() 函数。(参见 第 14.15 节,“信息函数”。)它返回 格式的值,指示来自匹配 user_name@host_nameuser 表行的 User 和 Host 值。假设 jeffrey 连接并发出以下查询
mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost |
+----------------+此处显示的结果表明,匹配的 user 表行具有空白的 User 列值。换句话说,服务器正在将 jeffrey 视为匿名用户。
诊断身份验证问题的另一种方法是打印出 user 表并手动对其进行排序,以查看第一个匹配项是在哪里发生的。