Documentation Home
MySQL 8.3 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 40.8Mb
PDF (A4) - 40.9Mb
Man Pages (TGZ) - 294.0Kb
Man Pages (Zip) - 409.0Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb
Excerpts from this Manual

MySQL 8.3 Reference Manual  /  ...  /  Access Control, Stage 1: Connection Verification

8.2.6 连接控制,第一阶段:连接验证

当您尝试连接到 MySQL 服务器时,服务器根据以下条件接受或拒绝连接:

  • 您的身份和您是否可以通过提供正确的凭证来验证身份。

  • 您的账户是否被锁定或解锁。

服务器首先检查凭证,然后检查账户锁定状态。如果在任何一步骤中失败,服务器将完全拒绝您访问。否则,服务器接受连接,然后进入第二阶段并等待请求。

服务器使用 user 表中的列来执行身份和凭证检查,只有在满足以下条件时才接受连接:

  • 客户端主机名和用户名与 HostUser 列在某个 user 表行中匹配。有关允许的 HostUser 值的规则,请参阅 第 8.2.4 节,“指定账户名称”

  • 客户端提供了行中指定的凭证(例如密码),如 authentication_string 列所示。凭证使用 plugin 列中指定的身份验证插件进行解释。

  • 行表明账户未锁定。锁定状态记录在 account_locked 列中,必须具有 'N' 值。账户锁定可以使用 CREATE USERALTER USER 语句设置或更改。

您的身份基于两方面信息:

  • 您的 MySQL 用户名。

  • 您连接的客户端主机。

如果 User 列值非空,则incoming 连接的用户名必须完全匹配。如果 User 值为空,则它将匹配任何用户名。如果 user 表行匹配 incoming 连接的用户名为空,则用户被认为是匿名用户,没有指定的用户名。这意味着在连接的整个过程中(即第二阶段),空用户名将用于所有进一步的访问检查。

认证字符串列可以为空。这不是通配符,不意味着任何密码都匹配。这意味着用户必须不指定密码连接。客户端认证插件实现的认证方法可能会或可能不会使用认证字符串列中的密码。在这种情况下,可能会使用外部密码来认证到 MySQL 服务器。

用户表中的认证字符串列中存储的非空密码值是加密的。MySQL 不会将密码存储为明文供任何人查看。相反,当用户尝试连接时,提供的密码将被加密(使用账户认证插件实现的密码哈希方法)。然后,在连接过程中,使用加密的密码来检查密码是否正确。这是在连接过程中不传输加密密码的情况下完成的。参见 第 8.2.1 节,“账户用户名和密码”

从 MySQL 服务器的角度来看,加密密码是真正的密码,因此您不应该将其提供给任何人。特别是,不要将非管理员用户的读取访问权限授予 mysql 系统数据库中的表

以下表格显示了用户表中的 User 和 Host 值的各种组合如何应用于传入连接。

User Value Host Value 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 198.51.100.177
'fred' '198.51.100.%' fred 198.51.100
'fred' '198.51.100.0/255.255.255.0'

当来自客户端的连接的主机名和用户名与user表中的多行匹配时,可能会出现这种情况:前面的示例演示了这种情况:其中一些条目显示来自h1.example.net的连接由fred发出。

当可能有多个匹配时,服务器必须确定使用哪一个。它按照以下方式解决这个问题:

  • 每当服务器将user表读入内存时,它都会对行进行排序。

  • 当客户端尝试连接时,服务器会按照排序顺序浏览行。

  • 服务器使用第一个匹配客户端主机名和用户名的行。

服务器使用排序规则,首先对具有最具体的Host值的行进行排序:

  • 字面IP地址和主机名是最具体的。

  • 具有IP地址的主机部分的账户按照以下顺序排序:

    • 具有主机部分给定为IP地址的账户:

      CREATE USER 'user_name'@'127.0.0.1';
      CREATE USER 'user_name'@'198.51.100.44';
    • 具有主机部分给定为IP地址使用CIDR表示法的账户:

      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%%)。

从MySQL 8.2.0开始,将'%'视为等同于localhost的处理方式已经弃用;您应该期望这种行为将从未来版本的MySQL中删除。

具有相同Host值的行按照最具体的User值排序。空User值表示任何用户,是最不具体的,因此对于具有相同Host值的行,非匿名用户排序在匿名用户之前。

对于具有相同Host值和User值的行,顺序是非确定性的。

要了解这如何工作,假设user表看起来像这样:

+-----------+----------+-
| Host      | User     | ...
+-----------+----------+-
| %         | root     | ...
| %         | jeffrey  | ...
| localhost | root     | ...
| localhost |          | ...
+-----------+----------+-

当服务器将表读入内存时,它使用刚才描述的规则对行进行排序。排序后的结果看起来像这样:

+-----------+----------+-
| Host      | User     | ...
+-----------+----------+-
| localhost | root     | ...
| localhost |          | ...
| %         | jeffrey  | ...
| %         | root     | ...
+-----------+----------+-

当客户端尝试连接时,服务器会浏览排序后的行,并使用找到的第一个匹配项。例如,对于来自 localhost 的连接,由 jeffrey,表中的两行匹配:一行的 HostUser 值为 'localhost''',另一行的值为 '%''jeffrey'。在排序顺序中,'localhost' 行首先出现,因此服务器使用该行。

这里还有另一个例子。假设 user 表看起来像这样:

+----------------+----------+-
| Host           | User     | ...
+----------------+----------+-
| %              | jeffrey  | ...
| h1.example.net |          | ...
+----------------+----------+-

排序后的表看起来像这样:

+----------------+----------+-
| Host           | User     | ...
+----------------+----------+-
| h1.example.net |          | ...
| %              | jeffrey  | ...
+----------------+----------+-

第一行匹配来自 h1.example.net 的任何用户的连接,而第二行匹配来自任何主机的 jeffrey 连接。

Note

人们常犯的一个错误是认为,对于给定的用户名,所有明确命名该用户的行都将首先用于服务器尝试连接匹配。这是不正确的。前面的示例说明了这一点,其中来自 h1.example.net 的连接由 jeffrey 首先匹配不是包含 'jeffrey' 作为 User 列值的行,而是没有用户名的行。因此,jeffrey 被认证为匿名用户,即使他在连接时指定了用户名。

如果您能够连接到服务器,但您的权限不是您期望的,那么您可能正在以其他账户身份进行认证。要找到服务器用于认证您的账户,请使用 CURRENT_USER() 函数。(见 第 14.15 节,“信息函数”。)它返回一个以 user_name@host_name 格式的值,指示匹配的 user 表行中的 UserHost 值。假设 jeffrey 连接并发出以下查询:

mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost     |
+----------------+

这里显示的结果表明,匹配的 user 表行具有空白的 User 列值。换言之,服务器正在将 jeffrey 视为匿名用户。

诊断认证问题的另一种方法是手动打印出 user 表,并手动排序以查看第一个匹配项是在哪里进行的。