Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


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

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

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

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

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

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

服务器使用 user 表中的列进行身份和凭证检查,只有满足这些条件时才接受连接:

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

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

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

您的身份基于两个信息:

  • 您的MySQL用户名。

  • 您连接的客户机主机。

如果User列值非空, incoming 连接的用户名必须完全匹配。如果User值为空,它将匹配任何用户名。如果 incoming 连接的user表行具有空用户名,则该用户被视为无名用户,而不是客户端实际指定的用户名。这意味着在连接期间(即 Stage 2),将使用空用户名进行所有进一步访问检查。

authentication_string列可以为空。这不是通配符,也不表示任何密码匹配。它表示用户必须无密码连接。该插件实现的身份验证方法可能会或不会在authentication_string列中使用密码。在这种情况下,外部密码也可能用于对 MySQL 服务器进行身份验证。

用户表中的非空密码值存储在 authentication_string 列中,MySQL 不将密码以明文形式存储,而是使用账户认证插件实现的加密方法对密码进行加密,然后在连接过程中检查密码是否正确。这个过程不需要加密后的密码传输。详见第8.2.1节,“帐户用户名和密码”

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

以下表格展示了 UserHost 值在用户表中的各种组合如何应用于 incoming 连接。

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.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' 与前一个示例相同

incoming连接的客户端主机名和用户名可能在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';
    • 主机部分使用CIDR notation给定的账户:

      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(socket文件、命名管道和共享内存)连接被视为本地连接,如果存在相应账户,或者否则匹配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的任何主机。

Note

人们常常误认为,服务器在尝试找到连接时,对于给定的用户名,所有明确命名该用户名的行都被使用。实际上,这不是真的。前面的示例就说明了这个问题,来自h1.example.netjeffrey连接首先匹配的是无用户名的行,而不是包含'jeffrey'User列值。因此,jeffrey被认证为匿名用户,即使他在连接时指定了用户名。

如果您可以连接到服务器,但权限不如预期,您可能被认证为其他账户。要找到服务器用来认证您的账户,使用CURRENT_USER()函数。(参见第14.15节,“信息函数”。)它返回一个以user_name@host_name格式的值,表示匹配的UserHost值来自user表中的行。假设jeffrey连接并执行以下查询:

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

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

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