Documentation Home

6.2.12 代理用户

MySQL 服务器使用身份验证插件对客户端连接进行身份验证。验证给定连接的插件可能会请求将连接(外部)用户视为不同的用户以进行权限检查。这使外部用户成为第二个用户的代理;也就是说,承担第二个用户的特权:

  • 外部用户是代理用户(可以冒充或成为另一个用户的用户)。

  • 第二个用户是代理用户(其身份和权限可以由代理用户承担的用户)。

本节介绍代理用户功能的工作原理。有关身份验证插件的一般信息,请参阅 第 6.2.11 节,“可插入身份验证”。有关特定插件的信息,请参阅第 6.4.1 节,“身份验证插件”。有关编写支持代理用户的身份验证插件的信息,请参阅 在身份验证插件中实现代理用户支持

代理用户支持要求

对于给定身份验证插件的代理,必须满足以下条件:

  • 该插件必须支持代理。

  • 外部代理用户的帐户必须设置为由插件进行身份验证。使用 CREATE USERor GRANT语句将帐户与身份验证插件相关联。

  • 代理用户的帐户必须存在并且被授予代理用户所承担的特权。为此使用 CREATE USERand GRANT语句。

  • 通常情况下,代理用户配置为只能用于代理场景,不能用于直接登录。

  • 代理用户帐户必须具有 PROXY代理帐户的权限。GRANT为此 使用 语句。

  • 对于连接到代理帐户的客户端被视为代理用户,身份验证插件必须返回一个不同于客户端用户名的用户名,以指示代理帐户的用户名,该用户名定义代理将承担的权限用户。

代理机制只允许将外部客户端用户名映射到代理用户名。没有映射主机名的规定:

  • 当客户端连接到服务器时,服务器根据客户端程序传递的用户名和客户端连接的主机来确定合适的帐户。

  • 如果该帐户是代理帐户,服务器会尝试通过使用身份验证插件返回的用户名和代理帐户的主机名查找代理帐户的匹配项来确定适当的代理帐户。代理帐户中的主机名将被忽略。

简单代理用户示例

考虑以下帐户定义:

-- create proxy account
CREATE USER 'employee_ext'@'localhost'
  IDENTIFIED WITH my_auth_plugin
  AS 'my_auth_string';

-- create proxied account and grant its privileges
CREATE USER 'employee'@'localhost'
  IDENTIFIED BY 'employee_password';
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'employeeemployee_extemployeeemployee_extemployee

在这种情况下,employee_ext是代理用户和employee被代理用户。

服务器 通过检查(代理用户)是否 具有(被代理用户)的 权限来 验证用户是否employee可以进行 代理身份验证。如果未授予此特权,则会发生错误。否则, 承担 的特权 。服务器根据授予的权限检查在客户端会话期间执行的语句 。在这种情况下, 可以访问 数据库中的表。 employee_extemployee_extPROXYemployeeemployee_extemployeeemployee_extemployeeemployee_extemployees

为确保被代理的账户employee不能直接使用,请勿将其密码告诉任何人。如果您不让任何人知道该帐户的密码,客户端将无法使用它直接连接到 MySQL 服务器。

当发生代理时,可以使用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 服务器。

授予和撤销 PROXY 特权

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';

MySQLGRANTREVOKE语法扩展照常工作。例子:

-- 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

  • 对于自身:对于帐户名的用户名和主机名部分, proxied_user的值必须与 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
CREATE USER 'developer'@'localhost'
  IDENTIFIED BY 'developer_password';
CREATE USER 'manager'@'localhost'
  IDENTIFIED BY 'manager_password';

-- grant to default proxy account the
-- PROXY privilege for proxied accounts
GRANT PROXY
  ON 'manager'@'localhost'
  TO ''@'';
GRANT PROXY
  ON 'developer'@'localhost'
  TO ''@'';

与前面示例中创建的代理帐户一样,密码应保密,以便客户端无法使用这些帐户直接登录到 MySQL 服务器。

现在假设客户端连接如下:

$> mysql --user=myuser --password ...
Enter password: myuser_password

服务器没有找到myuser定义为 MySQL 的用户。但是因为有一个''@''与客户端用户名和主机名匹配的空白用户帐户 ( ),服务器根据该帐户对客户端进行身份验证:服务器调用ldap_auth 身份验证插件并将myusermyuser_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 |
+------------------+-------------------+

为简单起见,外部身份验证不能是多级的:在前面的示例中既没有考虑用于的凭据developer也没有考虑用于的凭据。manager但是,如果客户端尝试连接并直接作为 developermanager 帐户进行身份验证,它们仍然会被使用,这就是为什么应该保护这些代理帐户免受直接登录的原因(请参阅 防止直接登录到代理帐户)。

默认代理用户和匿名用户冲突

如果您打算创建默认代理用户,请检查优先于默认代理用户 的其他现有匹配任何用户”帐户,因为它们可以阻止该用户按预期工作。

在前面的讨论中,默认代理用户帐户 ''在主机部分,匹配任何主机。如果设置默认代理用户,请注意检查是否存在具有相同用户部分和 '%'主机部分的非代理帐户,因为它 '%'也匹配任何主机,但优先于''服务器用于对帐户行进行排序的规则内部(参见 第 6.2.5 节“访问控制,第 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 语句修改为名称''@'localhost'而不是''@''作为代理用户。

    请注意,此策略可防止匿名用户连接来自localhost.

  • 使用命名默认帐户而不是匿名默认帐户。有关此技术的示例,请参阅使用 authentication_windows插件的说明。请参阅 第 6.4.1.7 节,“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 BY 'some_password';
    -- create proxied user for remote connections
    CREATE USER 'developer'@'%'
      IDENTIFIED BY 'some_password';

    授予每个代理帐户 PROXY相应代理帐户的权限:

    GRANT PROXY
      ON 'developer'@'localhost'
      TO ''@'localhost';
    GRANT PROXY
      ON 'developer'@'%'
      TO ''@'%';

    最后,将适当的权限授予本地和远程代理用户(未显示)。

    假设 some_plugin/ 组合导致将客户端用户名映射到。本地连接匹配 代理用户,映射到 代理用户。远程连接匹配代理用户,映射到 代理用户。 'some_auth_string'some_plugindeveloper''@'localhost''developer'@'localhost'''@'%''developer'@'%'

代理用户系统变量

两个系统变量有助于跟踪代理登录过程:

  • proxy_userNULL:如果不使用代理,则此值为 。否则,它表示代理用户帐户。例如,如果客户端通过''@'' 代理帐户进行身份验证,则此变量设置如下:

    mysql> SELECT @@proxy_user;
    +--------------+
    | @@proxy_user |
    +--------------+
    | ''@''        |
    +--------------+
  • external_user:有时身份验证插件可能会使用外部用户对 MySQL 服务器进行身份验证。例如,当使用 Windows 本机身份验证时,使用 Windows API 进行身份验证的插件不需要传递给它的登录 ID。但是,它仍然使用 Windows 用户 ID 进行身份验证。external_user插件可以使用只读会话变量将此外部用户 ID(或其前 512 个 UTF-8 字节)返回给服务器 。如果插件没有设置这个变量,它的值为 NULL.