文档首页
MySQL 9.0 参考手册
相关文档 下载本手册
PDF (US Ltr) - 40.0Mb
PDF (A4) - 40.1Mb
手册页 (TGZ) - 258.2Kb
手册页 (Zip) - 365.3Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 9.0 参考手册  /  ...  /  访问控制,阶段 1:连接验证

8.2.6 访问控制,阶段 1:连接验证

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

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

  • 您的帐户是锁定还是解锁。

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

服务器使用 user 表中的列执行身份和凭据检查,仅当满足以下条件时才接受连接

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

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

  • 该行指示帐户已解锁。锁定状态记录在 account_locked 列中,该列的值必须为 'N'。可以使用 CREATE USERALTER USER 语句设置或更改帐户锁定。

您的身份基于两条信息

  • 您的 MySQL 用户名。

  • 您连接的客户端主机。

如果 User 列值非空,则传入连接中的用户名必须完全匹配。如果 User 值为空,则它将匹配任何用户名。如果与传入连接匹配的 user 表行具有空用户名,则该用户被认为是匿名用户,没有姓名,而不是具有客户端实际指定的名称的用户。这意味着空用户名将用于传入连接期间的所有后续访问检查(即,在阶段 2 期间)。

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

存储在 user 表的 authentication_string 列中的非空密码值已加密。MySQL 不会以明文形式存储密码,以便任何人都能看到。相反,尝试连接的用户提供的密码将被加密(使用帐户身份验证插件实现的密码哈希方法)。然后,加密的密码将在连接过程中用于检查密码是否正确。这在加密密码从未经过连接的情况下完成。请参见 第 8.2.1 节,“帐户用户名和密码”

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

下表显示了 user 表中 UserHost 值的不同组合如何应用于传入连接。

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.netx.example.comx.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.netfred 的连接匹配。

当可能存在多个匹配项时,服务器必须确定使用哪一个。它通过以下方式解决此问题:

  • 每当服务器将 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 值的行,非匿名用户将排在匿名用户之前。

对于具有相同特定性 HostUser 值的行,排序是非确定性的。

为了了解它的工作原理,假设 user 表如下所示:

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

当服务器将表读入内存时,它会使用刚才描述的规则对行进行排序。排序后的结果如下所示:

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

当客户端尝试连接时,服务器会查看排序后的行并使用找到的第一个匹配项。对于来自 localhostjeffrey 的连接,表中的两行匹配: HostUser 值分别为 'localhost''' 的行,以及 '%''jeffrey' 的行。 'localhost' 行在排序顺序中排在最前面,因此服务器使用的是它。

以下是一个示例。假设 user 表如下所示:

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

排序后的表如下所示:

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

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

注意

对于给定的用户名,所有显式命名该用户的行在服务器尝试查找连接匹配项时首先使用,这是一个常见的误解。情况并非如此。前面的示例说明了这一点,其中来自 h1.example.netjeffrey 的连接首先匹配的不是包含 '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 表并手动对其进行排序,以查看第一个匹配项是在哪里发生的。