6.2.5 访问控制,第 1 阶段:连接验证
当您尝试连接到 MySQL 服务器时,服务器根据以下条件接受或拒绝连接:
-
您的身份以及是否可以通过提供正确的密码来验证您的身份
-
您的帐户是锁定还是未锁定
服务器首先检查凭据,然后检查帐户锁定状态。任一步骤失败都会导致服务器完全拒绝您的访问。否则,服务器将接受连接,然后进入阶段 2 并 await 请求。
使用三个user
table 范围列(Host
,User
和authentication_string
)执行凭据检查。锁定状态记录在user
tableaccount_locked
列中。仅当某些user
table 行中的Host
和User
列与 Client 端主机名和用户名匹配,Client 端提供该行中指定的密码且account_locked
值为'N'
时,服务器才接受连接。 第 6.2.4 节“指定帐户名”中给出了允许的Host
和User
值的规则。可以使用ALTER USER语句更改帐户锁定。
您的身份基于以下两条信息:
-
您连接的 Client 端主机
-
您的 MySQL 用户名
如果User
列的值是非空白的,则传入 Connecting 的用户名必须完全匹配。如果User
值为空白,则它与任何用户名匹配。如果与传入连接匹配的user
table 行的用户名为空,则该用户将被视为没有名称的匿名用户,而不是具有 Client 端实际指定的名称的用户。这意味着在连接持续时间内(即在第 2 阶段),将使用空白用户名进行所有进一步的访问检查。
authentication_string
列可以为空白。这不是通配符,并不意味着任何密码都匹配。这意味着用户必须在未指定密码的情况下进行连接。如果服务器使用插件对 Client 端进行身份验证,则该插件实现的身份验证方法可以使用也可以不使用authentication_string
列中的密码。在这种情况下,有可能还使用外部密码对 MySQL 服务器进行身份验证。
user
table 中的非空白authentication_string
值 table 示加密的密码。 MySQL 不会将密码存储为明文,任何人都可以看到。而是,将尝试连接的用户提供的密码进行加密(使用由帐户身份验证插件实现的密码哈希方法)。然后在检查密码是否正确时在连接过程中使用加密的密码。这样就无需在连接上传输加密密码。参见第 6.2.1 节“帐户用户名和密码”。
从 MySQL 的角度来看,加密的密码是* real *密码,因此您绝不应该允许任何人访问它。特别是,请勿授予非 Management 用户对mysql
系统数据库中的 table 的读取权限。
下 table 显示了user
table 中User
和Host
值的各种组合如何应用于传入连接。
User 值 |
Host 值 |
Permissible Connections |
---|---|---|
'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' |
与前面的示例相同 |
传入连接的 Client 端主机名和用户名可能与user
table 中的多行匹配。前面的示例集对此进行了演示:所示的几个条目匹配了h1.example.net
到fred
的连接。
当可能有多个匹配项时,服务器必须确定要使用哪个匹配项。它可以解决此问题,如下所示:
-
每当服务器将
user
table 读入内存时,它都会对行进行排序。 -
Client 端尝试连接时,服务器将按排序 Sequences 浏览各行。
-
服务器使用与 Client 端主机名和用户名匹配的第一行。
服务器使用排序规则,该规则首先对最具体的Host
值的行进行排序。Literals 主机名和 IP 地址是最具体的。 (LiteralsIP 地址的特异性不受其是否具有网络掩码的影响,因此198.51.100.13
和198.51.100.0/255.255.255.0
被认为是相同的.)模式'%'
table 示“任何主机”,并且是最不特定的。空字符串''
也 table 示“任何主机”,但在'%'
之后排序。具有相同Host
值的行将按照最具体的User
值进行排序(空白的User
值 table 示“任何用户”,并且是最不特定的)。对于具有相同Host
和User
值的行,Sequences 是不确定的。
要查看其工作原理,假设user
table 如下所示:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| % | root | ...
| % | jeffrey | ...
| localhost | root | ...
| localhost | | ...
+-----------+----------+-
当服务器将 table 读入内存时,它将使用刚刚描述的规则对行进行排序。排序后的结果如下所示:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| localhost | root | ...
| localhost | | ...
| % | jeffrey | ...
| % | root | ...
+-----------+----------+-
当 Client 端尝试连接时,服务器将浏览已排序的行并使用找到的第一个匹配项。对于从localhost
到jeffrey
的连接,table 中的两行匹配:Host
和User
值为'localhost'
和''
的那一行,以及'%'
和'jeffrey'
的值的那一行。 'localhost'
行首先以排序 Sequences 出现,因此这是服务器使用的行。
这是另一个例子。假设user
table 如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| % | jeffrey | ...
| h1.example.net | | ...
+----------------+----------+-
排序的 table 如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| h1.example.net | | ...
| % | jeffrey | ...
+----------------+----------+-
第一行匹配来自h1.example.net
的jeffrey
的连接,第二行匹配来自任何主机的jeffrey
的连接。
Note
一个常见的误解是认为,对于给定的用户名,当服务器尝试查找连接的匹配项时,首先使用显式命名该用户的所有行。这不是真的。前面的示例对此进行了说明,其中来自h1.example.net
到jeffrey
的连接首先不匹配包含'jeffrey'
作为User
列值的行,而是不匹配用户名的行。结果,jeffrey
被认证为匿名用户,即使他在连接时指定了用户名。
如果您能够连接到服务器,但是特权不是您所期望的,则您可能已通过其他帐户的身份验证。要找出服务器用来验证您身份的帐户,请使用CURRENT_USER()功能。 (请参阅第 12.15 节“信息功能”。)它以user_name@host_name
格式返回一个值,该值指示匹配的user
table 行中的User
和Host
值。假设jeffrey
连接并发出以下查询:
mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost |
+----------------+
此处显示的结果 table 明匹配的user
table 行具有空白User
列值。换句话说,服务器将jeffrey
视为匿名用户。
诊断身份验证问题的另一种方法是打印出user
table 并手动对其进行排序,以查看进行第一个匹配的位置。