MySQL 服务器使用身份验证插件对客户端连接进行身份验证。验证给定连接的插件可能会请求将连接(外部)用户视为不同的用户以进行权限检查。这使得外部用户可以成为第二个用户的代理;即承担第二个用户的权限:
- 外部用户是“代理用户”(可以冒充或成为其他用户的用户)。
- 第二个用户是“代理用户”(其身份和权限可以由代理用户承担的用户)。
本节介绍代理用户功能的工作原理。有关身份验证插件的一般信息,请参阅 第 6.2.17 节,“可插入身份验证”。有关特定插件的信息,请参阅第 6.4.1 节,“身份验证插件”。有关编写支持代理用户的身份验证插件的信息,请参阅 在身份验证插件中实现代理用户支持。
笔记
通过代理获得的一个管理好处是,DBA 可以设置具有一组特权的单个帐户,然后让多个代理用户拥有这些特权,而无需将特权单独分配给这些用户中的每一个。作为代理用户的替代方案,DBA 可能会发现角色提供了一种合适的方法来将用户映射到特定的命名权限集。每个用户都可以被授予给定的单个角色,从而实际上被授予适当的权限集。请参阅第 6.2.10 节,“使用角色”。
代理用户支持要求
要为给定的身份验证插件进行代理,必须满足以下条件:
-
必须由插件本身或代表插件的 MySQL 服务器支持代理。在后一种情况下,可能需要显式启用服务器支持;请参阅 代理用户映射的服务器支持。
-
外部代理用户的帐户必须设置为由插件进行身份验证。使用该
CREATE USER语句将帐户与身份验证插件相关联,或ALTER USER更改其插件。 -
代理用户的帐户必须存在并被授予代理用户所承担的权限。为此使用
CREATE USERandGRANT语句。 -
通常,代理用户被配置为只能用于代理场景,不能用于直接登录。
-
对于连接到代理帐户的客户端被视为代理用户,身份验证插件必须返回与客户端用户名不同的用户名,以指示定义代理承担的权限的代理帐户的用户名用户。
或者,对于由服务器提供代理映射的插件,代理用户
PROXY由代理用户持有的权限决定 。
代理机制只允许将外部客户端用户名映射到代理用户名。没有映射主机名的规定:
- 当客户端连接到服务器时,服务器根据客户端程序传递的用户名和客户端连接的主机确定正确的帐户。
- 如果该帐户是代理帐户,则服务器尝试通过使用身份验证插件返回的用户名和代理帐户的主机名查找代理帐户的匹配项来确定适当的代理帐户。代理帐户中的主机名将被忽略。
简单代理用户示例
考虑以下帐户定义:
-- create proxy account
CREATE USER 'employee_ext'@'localhost'
IDENTIFIED WITH my_auth_plugin
AS 'my_auth_string';
-- create proxied account and grant its privileges;
-- use mysql_no_login plugin to prevent direct login
CREATE USER 'employee'@'localhost'
IDENTIFIED WITH mysql_no_login;
GRANT ALL
ON employees.*
TO 'employee'@'localhost';
-- grant to proxy account the
-- PROXY privilege for proxied account
GRANT PROXY
ON 'employee'@'localhost'
TO 'employee_ext'@'localhost';
当客户端employee_ext从本地主机连接时,MySQL 使用命名的插件 my_auth_plugin来执行身份验证。假设根据内容 并可能通过咨询某些外部身份验证系统向服务器 my_auth_plugin返回用户名。该名称与 不同 ,因此返回 作为对服务器的请求,以便出于权限检查的目的将外部用户视为 本地用户。 employee``'*my_auth_string*'``employee``employee_ext``employee``employee_ext``employee
在这种情况下,employee_ext是代理用户,employee是代理用户。
服务器通过检查(代理用户)是否具有(被代理用户)的 权限来 验证用户是否 employee可以进行 代理认证 。如果尚未授予此权限,则会发生错误。否则, 承担 的特权 。服务器根据授予的权限检查在客户端会话期间执行的语句 。在这种情况下, 可以访问数据库中的表 。 employee_ext``employee_extPROXYemployee``employee_ext``employee``employee_ext``employee``employee_ext``employees
代理帐户employee使用 mysql_no_login身份验证插件来防止客户端使用该帐户直接登录。(这假设插件已安装。有关说明,请参阅 第 6.4.1.9 节,“无登录可插拔身份验证”。)有关保护代理帐户不被直接使用的替代方法,请参阅 防止直接登录到代理帐户。
代理发生时,可以使用USER() 和CURRENT_USER()函数查看连接用户(代理用户)和当前会话期间应用权限的帐户(代理用户)之间的区别。对于刚刚描述的示例,这些函数返回以下值:
mysql> SELECT USER(), CURRENT_USER();
+------------------------+--------------------+
| USER() | CURRENT_USER() |
+------------------------+--------------------+
| employee_ext@localhost | employee@localhost |
+------------------------+--------------------+
在CREATE USER创建代理用户帐户的IDENTIFIED WITH语句中,命名支持代理的身份验证插件的子句后跟可选的子句,指定用户连接时服务器传递给插件的字符串。如果存在,该字符串提供的信息可帮助插件确定如何将代理(外部)客户端用户名映射到代理用户名。是否需要该子句取决于每个插件。如果是这样,身份验证字符串的格式取决于插件打算如何使用它。有关给定插件接受的身份验证字符串值的信息,请参阅给定插件的文档。 AS '*auth_string*'``AS
防止直接登录到代理帐户
代理帐户通常仅通过代理帐户使用。也就是说,客户端使用代理帐户进行连接,然后映射到并承担适当的代理用户的权限。
有多种方法可以确保不能直接使用代理帐户:
- 将帐户与
mysql_no_login身份验证插件相关联。在这种情况下,该帐户在任何情况下都不能用于直接登录。这假设插件已安装。有关说明,请参阅 第 6.4.1.9 节,“免登录可插入身份验证”。 ACCOUNT LOCK创建帐户时 包括该选项。见第 13.7.1.3 节,“CREATE USER 语句”。使用此方法,还包括一个密码,以便以后解锁帐户时,没有密码就无法访问它。(如果该validate_password组件已启用,则不允许创建没有密码的帐户,即使帐户已锁定。请参阅 第 6.4.3 节,“密码验证组件”。)- 使用密码创建帐户,但不要将密码告诉其他任何人。如果您不让任何人知道该帐户的密码,则客户端无法使用它直接连接到 MySQL 服务器。
授予和撤销代理权限
PROXY需要 该权限才能使外部用户能够以其他用户的身份进行连接并拥有该用户的权限。要授予此权限,请使用该 GRANT语句。例如:
GRANT PROXY ON 'proxied_user' TO 'proxy_user';
该语句在mysql.proxies_priv授权表中创建一行 。
在连接时,*proxy_user*必须代表有效的外部身份验证 MySQL 用户,并且 *proxied_user*必须代表有效的本地身份验证用户。否则,连接尝试失败。
对应的REVOKE语法是:
REVOKE PROXY ON 'proxied_user' FROM 'proxy_user';
MySQLGRANT和 REVOKE语法扩展照常工作。例子:
-- grant PROXY to multiple accounts
GRANT PROXY ON 'a' TO 'b', 'c', 'd';
-- revoke PROXY from multiple accounts
REVOKE PROXY ON 'a' FROM 'b', 'c', 'd';
-- grant PROXY to an account and enable the account to grant
-- PROXY to the proxied account
GRANT PROXY ON 'a' TO 'd' WITH GRANT OPTION;
-- grant PROXY to default proxy account
GRANT PROXY ON 'a' TO ''@'';
该PROXY特权可以在这些情况下被授予:
- 由具有
GRANT PROXY ... WITH GRANT OPTIONfor 的用户*proxied_user*。 - By *
proxied_user*for自身:对于帐户名的用户名和主机名部分, 的值USER()必须完全匹配CURRENT_USER()和proxied_user。
rootMySQL 安装过程中创建 的初始帐户具有 PROXY ... WITH GRANT OPTION权限''@'',即所有用户和所有主机。这允许 root设置代理用户,以及将设置代理用户的权限委托给其他帐户。例如,root可以这样做:
CREATE USER 'admin'@'localhost'
IDENTIFIED BY 'admin_password';
GRANT PROXY
ON ''@''
TO 'admin'@'localhost'
WITH GRANT OPTION;
这些语句创建一个admin可以管理所有GRANT PROXY映射的用户。例如,admin可以这样做:
GRANT PROXY ON sally TO joe;
默认代理用户
要指定部分或所有用户应使用给定的身份验证插件进行连接,请创建一个具有空用户名和主机名 ( )的“空白” MySQL 帐户,将''@''其与该插件关联,并让插件返回真实的经过身份验证的用户名 (如果与空白用户不同)。假设存在一个名为的插件ldap_auth,它实现 LDAP 身份验证并将连接用户映射到开发人员或经理帐户。要在这些帐户上设置用户代理,请使用以下语句:
-- create default proxy account
CREATE USER ''@''
IDENTIFIED WITH ldap_auth
AS 'O=Oracle, OU=MySQL';
-- create proxied accounts; use
-- mysql_no_login plugin to prevent direct login
CREATE USER 'developer'@'localhost'
IDENTIFIED WITH mysql_no_login;
CREATE USER 'manager'@'localhost'
IDENTIFIED WITH mysql_no_login;
-- grant to default proxy account the
-- PROXY privilege for proxied accounts
GRANT PROXY
ON 'manager'@'localhost'
TO ''@'';
GRANT PROXY
ON 'developer'@'localhost'
TO ''@'';
现在假设客户端连接如下:
shell> mysql --user=myuser --password ...
Enter password: myuser_password
服务器未找到myuser定义为 MySQL 用户,但由于存在''@''与客户端用户名和主机名匹配的空白用户帐户 ( ),服务器根据该帐户对客户端进行身份验证。服务器调用ldap_auth 身份验证插件并将myuser和 *myuser_password*作为用户名和密码传递给它。
如果ldap_auth插件在 LDAP 目录中发现*myuser_password*不是正确的密码myuser,则身份验证失败并且服务器拒绝连接。
如果密码正确,ldap_auth 发现myuser是开发者,则返回用户名developer给MySQL服务器,而不是myuser. 将与myuser 信号的客户端用户名不同的用户名返回给它应该 myuser视为代理的服务器。服务器验证 ''@''可以认证为 developer(因为''@'' 有PROXY权限这样做)并接受连接。会话继续拥有 代理用户myuser的权限 developer。(这些权限应该由 DBA 使用 GRANT语句设置,未显示。) USER()和 CURRENT_USER() 函数返回这些值:
mysql> SELECT USER(), CURRENT_USER();
+------------------+---------------------+
| USER() | CURRENT_USER() |
+------------------+---------------------+
| myuser@localhost | developer@localhost |
+------------------+---------------------+
如果插件在 LDAP 目录中找到 myuser管理员,它会返回 manager用户名,会话继续使用代理用户myuser的权限manager。
mysql> SELECT USER(), CURRENT_USER();
+------------------+-------------------+
| USER() | CURRENT_USER() |
+------------------+-------------------+
| myuser@localhost | manager@localhost |
+------------------+-------------------+
为简单起见,外部身份验证不能是多级的:前面的示例中既没有考虑 for 的凭据,developer也没有manager考虑for的凭据。但是,如果客户端尝试直接作为developer或manager 帐户进行连接和身份验证,它们仍然会被使用 ,这就是为什么这些代理帐户应该受到保护以防止直接登录(请参阅 防止直接登录到代理帐户)。
默认代理用户和匿名用户冲突
如果您打算创建默认代理用户,请检查优先于默认代理用户的其他现有“匹配任何用户”帐户,因为它们可以阻止该用户按预期工作。
在前面的讨论中,默认代理用户帐户 ''在主机部分,匹配任何主机。如果您设置了默认代理用户,还要注意检查是否存在具有相同用户部分和'%'主机部分的非代理帐户 ,因为 '%'也匹配任何主机,但优先于''服务器用于对帐户行进行排序的规则内部(参见 第 6.2.6 节,“访问控制,阶段 1:连接验证”)。
假设 MySQL 安装包括这两个帐户:
-- create default proxy account
CREATE USER ''@''
IDENTIFIED WITH some_plugin
AS 'some_auth_string';
-- create anonymous account
CREATE USER ''@'%'
IDENTIFIED BY 'anon_user_password';
第一个帐户 ( ''@'') 用作默认代理用户,用于验证与更具体帐户不匹配的用户的连接。第二个帐户 ( ''@'%') 是一个匿名用户帐户,例如,它可能是为了让没有自己帐户的用户能够匿名连接而创建的。
两个帐户具有相同的用户部分 ( ''),它匹配任何用户。并且每个帐户都有一个与任何主机匹配的主机部分。然而,连接尝试的帐户匹配具有优先级,因为匹配规则'%'将''. 对于不匹配任何更特定帐户的帐户,服务器尝试针对''@'%'(匿名用户)而不是 ''@''(默认代理用户)对它们进行身份验证 。因此,从不使用默认代理帐户。
要避免此问题,请使用以下策略之一:
-
删除匿名帐户,使其与默认代理用户不冲突。
-
使用在匿名用户之前匹配的更具体的默认代理用户。例如,要仅允许
localhost代理连接,请使用''@'localhost':CREATE USER ''@'localhost' IDENTIFIED WITH some_plugin AS 'some_auth_string';此外,将任何
GRANT PROXY语句修改为 name''@'localhost'而不是''@''代理用户。请注意,此策略可防止匿名用户连接来自
localhost. -
使用命名的默认帐户而不是匿名的默认帐户。有关此技术的示例,请参阅
authentication_windows插件使用说明 。请参见 第 6.4.1.6 节,“Windows 可插入身份验证”。 -
创建多个代理用户,一个用于本地连接,一个用于“其他所有”(远程连接)。这在本地用户应该具有与远程用户不同的权限时尤其有用。
创建代理用户:
-- create proxy user for local connections CREATE USER ''@'localhost' IDENTIFIED WITH some_plugin AS 'some_auth_string'; -- create proxy user for remote connections CREATE USER ''@'%' IDENTIFIED WITH some_plugin AS 'some_auth_string';创建代理用户:
-- create proxied user for local connections CREATE USER 'developer'@'localhost' IDENTIFIED WITH mysql_no_login; -- create proxied user for remote connections CREATE USER 'developer'@'%' IDENTIFIED WITH mysql_no_login;为每个代理帐户
PROXY授予相应代理帐户的 权限:GRANT PROXY ON 'developer'@'localhost' TO ''@'localhost'; GRANT PROXY ON 'developer'@'%' TO ''@'%';最后,授予本地和远程代理用户适当的权限(未显示)。
假设
some_plugin/ 组合导致将客户端用户名映射到。本地连接匹配 代理用户,代理用户映射到 代理用户。远程连接匹配代理用户,代理用户映射到 代理用户。'*some_auth_string*'``some_plugin``developer``''@'localhost'``'developer'@'localhost'``''@'%'``'developer'@'%'
代理用户映射的服务器支持
一些身份验证插件为自己实现代理用户映射(例如,PAM 和 Windows 身份验证插件)。其他认证插件默认不支持代理用户。其中,有些可以请求 MySQL 服务器本身根据授予的代理权限映射代理用户:mysql_native_password, sha256_password. 如果check_proxy_users启用了 系统变量,服务器会为发出此类请求的任何身份验证插件执行代理用户映射:
-
默认情况下,
check_proxy_users禁用,因此即使对于请求服务器支持代理用户的身份验证插件,服务器也不执行代理用户映射。 -
如果
check_proxy_users启用,可能还需要启用特定于插件的系统变量以利用服务器代理用户映射支持:-
对于
mysql_native_password插件,启用mysql_native_password_proxy_users. -
对于
sha256_password插件,启用sha256_password_proxy_users.
-
例如,要启用所有上述功能,请使用my.cnf文件中的以下行启动服务器:
[mysqld]
check_proxy_users=ON
mysql_native_password_proxy_users=ON
sha256_password_proxy_users=ON
假设已启用相关系统变量,使用 像往常一样创建代理用户CREATE USER,然后将其授予PROXY其他单个帐户的 权限,以将其视为代理用户。当服务器收到代理用户的成功连接请求时,发现该用户具有该PROXY 权限,并使用它来确定合适的代理用户。
-- create proxy account
CREATE USER 'proxy_user'@'localhost'
IDENTIFIED WITH mysql_native_password
BY 'password';
-- create proxied account and grant its privileges;
-- use mysql_no_login plugin to prevent direct login
CREATE USER 'proxied_user'@'localhost'
IDENTIFIED WITH mysql_no_login;
-- grant privileges to proxied account
GRANT ...
ON ...
TO 'proxied_user'@'localhost';
-- grant to proxy account the
-- PROXY privilege for proxied account
GRANT PROXY
ON 'proxied_user'@'localhost'
TO 'proxy_user'@'localhost';
要使用代理帐户,请使用其名称和密码连接到服务器:
shell> mysql -u proxy_user -p
Enter password: (enter proxy_user password here)
认证成功,服务器发现 proxy_user有 PROXY权限为 proxied_user,会话继续进行 proxy_user权限为 proxied_user。
服务器执行的代理用户映射受以下限制:
- 即使
PROXY授予了相关权限,服务器也不会代理匿名用户或从匿名用户代理。 - 当一个帐户被授予多个代理帐户的代理权限时,服务器代理用户映射是不确定的。因此,不鼓励为多个代理帐户授予单个帐户代理权限。
代理用户系统变量
两个系统变量有助于跟踪代理登录过程:
-
proxy_user:这个值是NULL在不使用代理的情况下。否则,它表示代理用户帐户。例如,如果客户端通过''@''代理帐户进行身份验证,则此变量设置如下:mysql> SELECT @@proxy_user; +--------------+ | @@proxy_user | +--------------+ | ''@'' | +--------------+ -
external_user:有时身份验证插件可能会使用外部用户对 MySQL 服务器进行身份验证。例如,当使用 Windows 本机身份验证时,使用 Windows API 进行身份验证的插件不需要传递给它的登录 ID。但是,它仍然使用 Windows 用户 ID 进行身份验证。插件可以使用external_user只读会话变量将此外部用户 ID(或其前 512 个 UTF-8 字节)返回到服务器 。如果插件没有设置这个变量,它的值为NULL。




