### 6.2.6 访问控制，第 1 阶段：连接验证

[](<>)[](<>)[](<>)

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

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

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

    服务器首先检查凭据，然后检查帐户锁定状态。任一步骤的失败都会导致服务器完全拒绝您的访问。否则，服务器接受连接，然后进入第 2 阶段并等待请求。

    服务器使用表中的列执行身份和凭据检查`用户`表，仅在满足以下条件时才接受连接：

-   客户端主机名和用户名匹配`主持人`和`用户`一些列`用户`表行。对于允许的规则`主持人`和`用户`值，见[第 6.2.4 节，“指定帐户名称”](account-names.html).

-   客户端提供行中指定的凭据（例如，密码），如`身份验证字符串`柱子。使用在`插入`柱子。

-   该行表示该帐户已解锁。锁定状态记录在`帐户被锁定`列，其值必须为`'N'`.可以设置或更改帐户锁定[`创建用户`](create-user.html)要么[`更改用户`](alter-user.html)陈述。

    您的身份基于两条信息：

-   您的 MySQL 用户名。

-   您连接的客户端主机。

[](<>)

如果`用户`列值非空，传入连接中的用户名必须完全匹配。如果`用户`值为空，它匹配任何用户名。如果`用户`匹配传入连接的表行有一个空白用户名，该用户被认为是一个没有名字的匿名用户，而不是客户端实际指定的名字的用户。这意味着在连接期间（即在第 2 阶段）使用空白用户名进行所有进一步的访问检查。

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

非空密码值存储在`身份验证字符串`的列`用户`表被加密。MySQL 不会将密码存储为明文供任何人查看。相反，尝试连接的用户提供的密码是加密的（使用帐户身份验证插件实现的密码散列方法）。然后在连接过程中检查密码是否正确时使用加密的密码。这是在没有加密密码通过连接传输的情况下完成的。看[第 6.2.1 节，“帐户用户名和密码”](user-names.html).

从 MySQL 的角度来看，加密的密码是*真实的*密码，所以你不应该让任何人访问它。特别是，*不要授予非管理用户对表中的表的读取权限`mysql`系统数据库*.

下表显示了各种组合如何`用户`和`主持人`中的值`用户`表适用于传入连接。

| `用户`价值 | `主持人`价值 | 允许的连接 |
| ------ | ------- | ----- |
| `“弗雷德”` | `'h1.example.net'` | `弗雷德`, 连接自`h1.example.net` |
| `''` | `'h1.example.net'` | 任何用户，从`h1.example.net` |
| `“弗雷德”` | `'%'` | `弗雷德`, 从任何主机连接 |
| `''` | `'%'` | 任何用户，从任何主机连接 |
| `“弗雷德”` | `'%.example.net'` | `弗雷德`，从任何主机连接`例子.net`领域 |
| `“弗雷德”` | `'x.example.%'` | `弗雷德`, 连接自`x.example.net`,`x.example.com`,`x.example.edu`， 等等;这可能没用 |
| `“弗雷德”` | `'198.51.100.177'` | `弗雷德`, 从带有 IP 地址的主机连接`198.51.100.177` |
| `“弗雷德”` | `'198.51.100.%'` | `弗雷德`，从任何主机连接`198.51.100`C类子网 |
| `“弗雷德”` | `'198.51.100.0/255.255.255.0'` | 和前面的例子一样 |

传入连接的客户端主机名和用户名可能匹配多于一行`用户`桌子。前面的一组示例证明了这一点：显示的几个条目与来自的连接匹配`h1.example.net`经过`弗雷德`.

当可能有多个匹配时，服务器必须确定使用哪一个。它解决了这个问题，如下所示：

-   每当服务器读取`用户`表到内存中，它对行进行排序。

-   当客户端尝试连接时，服务器会按排序顺序查看行。

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

    服务器使用排序规则对最具体的行进行排序`主持人`价值观第一：

-   文字 IP 地址和主机名是最具体的。

-   在 MySQL 8.0.23 之前，文字 IP 地址的特异性不受其是否具有网络掩码的影响，因此`198.51.100.13`和`198.51.100.0/255.255.255.0`被认为同样具体。从 MySQL 8.0.23 开始，在主机部分具有 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（套接字文件、命名管道和共享内存）连接被视为本地连接并匹配`本地主机`如果有任何这样的帐户，或者主机部分的通配符匹配`本地主机`否则（例如，`当地的％`,`l%`,`%`）。

    相同的行`主持人`值按最具体的排序`用户`价值观第一。一个空白`用户`value 表示“任何用户”并且是最不具体的，因此对于具有相同`主持人`值，非匿名用户排在匿名用户之前。

    对于同样特定的行`主持人`和`用户`值，顺序是不确定的。

    要了解这是如何工作的，假设`用户`表如下所示：


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

当服务器将表读入内存时，它使用刚刚描述的规则对行进行排序。排序后的结果如下：

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

[](<>)[](<>)[](<>)

当客户端尝试连接时，服务器会查看已排序的行并使用找到的第一个匹配项。对于来自的连接`本地主机`经过`杰弗里`，表中的两行匹配：与`主持人`和`用户`的值`'本地主机'`和`''`, 和一个值为`'%'`和`'杰弗里'`.这`'本地主机'`row 按排序顺序首先出现，因此这是服务器使用的。

这是另一个例子。假设`用户`表如下所示：

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

排序后的表如下所示：

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

第一行匹配来自任何用户的连接`h1.example.net`，而第二行匹配一个连接`杰弗里`从任何主机。

笔记

一种常见的误解是，对于给定的用户名，当服务器尝试查找连接的匹配项时，首先使用明确命名该用户的所有行。这不是真的。前面的示例说明了这一点，其中一个连接来自`h1.example.net`经过`杰弗里`首先不匹配包含的行`'杰弗里'`作为`用户`列值，但按没有用户名的行。因此，`杰弗里`被认证为匿名用户，即使他在连接时指定了用户名。

如果您能够连接到服务器，但您的权限不是您所期望的，您可能正在被验证为其他帐户。要找出服务器用来对您进行身份验证的帐户，请使用[`当前用户（）`](information-functions.html#function_current-user)功能。（看[第 12.16 节，“信息功能”](information-functions.html).) 它返回一个值`*`用户名`*@*`主机名`*`表示的格式`用户`和`主持人`匹配的值`用户`表行。假设`杰弗里`连接并发出以下查询：

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

这里显示的结果表明匹配`用户`表格行有一个空白`用户`列值。换句话说，服务器正在处理`杰弗里`作为匿名用户。

诊断身份验证问题的另一种方法是打印出`用户`表并手动对其进行排序以查看第一个匹配项的位置。
