当您尝试连接到MySQL服务器时,服务器会根据以下条件接受或拒绝连接:
-
您的身份以及您是否可以通过提供适当的凭据来验证它。
-
您的帐户是否已锁定或解锁。
服务器首先检查凭据,然后检查帐户锁定状态。任一步骤的失败都会导致服务器完全拒绝您的访问。否则,服务器接受连接,然后进入阶段2并等待请求。
服务器使用user
表中数据执行身份和凭据检查,仅在满足以下条件时才接受连接:
- 客户端主机名和用户名必须匹配到
user
表行中的Host
和User
列。 -
客户端提供行中指定的凭据(例如,密码),如
authentication_string
列所示。凭据使用plugin
列中命名的鉴权插件进行解释。 - 锁定状态记录在account_locked列中,其值必须为'N'。可以使用CREATE USER或ALTER USER语句设置或更改帐户锁定状态。
您的身份基于两条信息:
-
您的MySQL用户名。
-
您连接的客户端主机。
如果User
列值为非空白值,则传入连接中的用户名必须精确匹配。如果User值为空白值,则与任何用户名匹配。如果与传入连接匹配的用户
表行中的用户名为空白,则该用户被视为没有名称的匿名用户,而不是客户端实际指定名称的用户。这意味着在连接期间(即阶段2期间),所有进一步的访问检查都使用空白用户名。
authentication_string
列可以为空,这不是通配符,也不意味着任何密码都匹配,这意味着用户必须在不指定密码的情况下进行连接。由验证客户端的插件实现的鉴权方法可能会也可能不会使用authentication_string
列中的密码。在这种情况下,也有可能使用外部密码向MySQL服务器进行身份验证。
存储在user表authentication_string列中的非空白密码值是加密的。MySQL不会将密码以明文形式存储,这样任何人都可以看到是不安全的。相反,尝试连接的用户提供的密码是加密的(使用账号鉴权插件实现的密码散列方法)。然后在连接过程中检查密码是否正确时使用加密的密码。
从MySQL服务器的角度来看,加密密码是真正的密码,因此您永远不应该授予任何人访问权限。特别是,不要授予非管理用户对mysql
系统数据库中表的读取权限。
下表显示了user
表中User
和Host
值的各种组合如何应用于传入连接。
user列值 | Host列值 | 连接权限 |
---|---|---|
'fred' | 'h1.example.net' | 允许fred 从h1.example.net连接到mysql服务器 |
'' | 'h1.example.net' | 允许任何用户从 h1.example.net连接到mysql服务器 |
'fred' | '%' | 允许fred从任何主机连接到mysql服务器 |
'' | '%' | 允许任何用户从任何主机连接到mysql服务器 |
'fred' | '%.example.net' | 允许fred从 example.net 域名任何主机连接到mysql服务器 |
'fred' | 'x.example.%' | fred , connecting from x.example.net , x.example.com , x.example.edu , and so on; this is probably not useful |
'fred' | '198.51.100.177' | 允许fred从 198.51.100.177连接到mysql服务器 |
'fred' | '198.51.100.%' | fred , connecting from any host in the 198.51.100 class C subnet |
'fred' | '198.51.100.0/255.255.255.0' | Same as previous example |
传入连接的客户端主机名和用户名可能与user表中的多行数据匹配。当可能存在多个匹配时,服务器必须确定使用其中的哪一个。服务器如何选择呢,如下所示:
-
每当服务器将
user
表读入内存时,它都会对行进行排序。 -
当客户端尝试连接时,服务器会按排序顺序查看行。
-
服务器使用与客户端主机名和用户名匹配的第一行。
服务器排序规则首先对具有最具体的Host
值的行进行排序:
-
字面IP地址和主机名是最具体的。
-
在主机部分具有IP地址的帐户具有以下特定顺序:
1、将主机部分作为IP地址提供的帐户:
CREATE USER 'user_name'@'127.0.0.1'; CREATE USER 'user_name'@'198.51.100.44';
2、使用CIDR表示法将主机部分作为IP地址给出的帐户:
CREATE USER 'user_name'@'192.0.2.21/8'; CREATE USER 'user_name'@'198.51.100.44/16';
3、主机部分作为带有子网掩码的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
值的行,非匿名用户在匿名用户之前排序。
对于具有相同特定Host
和User
值的行,顺序是不确定的。
要了解这是如何工作的,假设user
表如下所示:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| % | root | ...
| % | jeffrey | ...
| localhost | root | ...
| localhost | | ...
+-----------+----------+-
当服务器将表读入内存时,它使用刚才描述的规则对行进行排序。排序后的结果如下所示:
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| localhost | root | ...
| localhost | | ...
| % | jeffrey | ...
| % | root | ...
+-----------+----------+-
当客户端尝试连接时,服务器会查看已排序的行并使用找到的第一个匹配。对于来自jeffrey
localhost
的连接,表中的两行匹配:Host
和User
值为'localhost'
和''
的行,以及值为'%'
和'jeffrey'
的行按排序顺序首先出现,因此服务器使用'localhost'
行判断是否可以连接mysql服务器。
另一个例子。假设user
表如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| % | jeffrey | ...
| h1.example.net | | ...
+----------------+----------+-
排序后的表如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| h1.example.net | | ...
| % | jeffrey | ...
+----------------+----------+-
第一行匹配来自h1.example.net
的任何用户的连接,而第二行匹配来自任何主机的jeffrey
的连接。
注:对于给定的用户名,当服务器试图找到匹配的连接时,首先使用所有显式命名该用户的行。这不是真的。前面的示例说明了这一点,其中来自jeffrey
的h1.example.net
连接首先匹配的不是包含'jeffrey'
作为User
列值的行,而是没有用户名的行。因此,jeffrey
被验证为匿名用户,即使他在连接时指定了用户名。
如果您能够连接到服务器,但您的权限不是您所期望的,您可能正在以其他帐户的身份进行身份验证。要找出服务器用于对您进行身份验证的帐户,请使用CURRENT_USER()函数。它返回一个user_name@host_name格式的值,指示User和Host表行。假设匹配jeffrey中的user值连接并发出以下查询:
mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost |
+----------------+
这里显示的结果表明匹配的user
表行有一个空白的User
列值。换句话说,服务器将jeffrey
视为匿名用户。
诊断鉴权问题的另一种方法是打印出user
表并手动排序,以查看第一行匹配的位置。