文档首页
MySQL 8.4 参考手册
相关文档 下载本手册

MySQL 8.4 参考手册  /  ...  /  代理用户

8.2.19 代理用户

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

  • 外部用户是“代理用户”(可以模拟或成为另一个用户的用户)。

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

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

注意

代理的一项管理优势是,DBA 可以设置一个具有一组权限的帐户,然后允许多个代理用户拥有这些权限,而无需单独为每个用户分配权限。作为代理用户的替代方案,DBA 可能会发现角色提供了一种将用户映射到特定命名权限集的合适方法。可以为每个用户授予给定的单个角色,以便有效地授予相应的权限集。请参阅 第 8.2.10 节,“使用角色”

代理用户支持的要求

要使给定身份验证插件发生代理,必须满足以下条件:

  • 必须支持代理,由插件本身或代表插件的 MySQL 服务器支持。在后一种情况下,可能需要显式启用服务器支持;请参阅 服务器对代理用户映射的支持

  • 必须设置外部代理用户的帐户以通过插件进行身份验证。使用 CREATE USER 语句将帐户与身份验证插件关联,或使用 ALTER USER 更改其插件。

  • 被代理用户的帐户必须存在,并且必须被授予代理用户要承担的权限。为此,请使用 CREATE USERGRANT 语句。

  • 通常,被代理用户的配置方式是,它只能在代理场景中使用,而不能用于直接登录。

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

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

    或者,对于服务器提供代理映射的插件,代理用户由代理用户持有的 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 根据 'my_auth_string' 的内容(可能还会参考一些外部身份验证系统)向服务器返回用户名 employee。名称 employeeemployee_ext 不同,因此返回 employee 相当于向服务器发出请求,以便在权限检查时将 employee_ext 外部用户视为 employee 本地用户。

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

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

被代理帐户 employee 使用 mysql_no_login 身份验证插件来防止客户端使用该帐户直接登录。(这假定已安装该插件。有关说明,请参见 第 8.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 身份验证插件相关联。在这种情况下,该帐户在任何情况下都不能用于直接登录。这假定已安装该插件。有关说明,请参见 第 8.4.1.9 节“无登录可插拔身份验证”

  • 创建帐户时包含 ACCOUNT LOCK 选项。请参见 第 15.7.1.3 节“CREATE USER 语句”。使用此方法时,还要包含一个密码,以便如果以后解锁该帐户,则不能在没有密码的情况下访问它。(如果启用了 validate_password 组件,则即使该帐户被锁定,也不允许创建没有密码的帐户。请参见 第 8.4.3 节“密码验证组件”。)

  • 使用密码创建帐户,但不要将密码告诉任何人。如果您不让任何人知道该帐户的密码,则客户端将无法使用它直接连接到 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';

MySQL GRANTREVOKE 语法扩展照常工作。示例。

-- 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 权限。

  • 由对 proxied_user 拥有 GRANT PROXY ... WITH GRANT OPTION 权限的用户授予。

  • proxied_user 为自身授予:USER() 的值必须与 CURRENT_USER()proxied_user 完全匹配,包括帐户名称的用户名和主机名部分。

在 MySQL 安装期间创建的初始 root 帐户对 ''@''(即所有用户和所有主机)拥有 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 ''@'';

现在假设客户端按如下方式连接。

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

服务器找不到定义为 MySQL 用户的 myuser,但由于存在与客户端用户名和主机名匹配的空白用户帐户(''@''),因此服务器将根据该帐户对客户端进行身份验证。服务器调用 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 |
+------------------+-------------------+

为简单起见,外部身份验证不能是多级的:在前面的示例中,developermanager 的凭据都不会被考虑在内。但是,如果客户端尝试直接以 developermanager 帐户连接并进行身份验证,则仍会使用这些凭据,这就是为什么应保护这些被代理帐户以防止直接登录(请参见 防止直接登录到被代理帐户)。

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

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

在前面的讨论中,默认代理用户帐户的主机部分为 '',这与任何主机都匹配。如果设置默认代理用户,请注意还要检查是否存在用户部分相同且主机部分为 '%' 的非代理帐户,因为 '%' 也与任何主机匹配,但根据服务器用于对帐户行进行内部排序的规则,'%' 优先于 ''(请参见 第 8.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 语句以将 ''@'localhost' 而不是 ''@'' 命名为代理用户。

    请注意,此策略会阻止来自 localhost 的匿名用户连接。

  • 使用命名的默认帐户,而不是匿名的默认帐户。有关此技术的示例,请参阅有关使用 authentication_windows 插件的说明。请参阅第 8.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,则可能还需要启用特定于插件的系统变量才能利用服务器代理用户映射支持

例如,要启用所有上述功能,请在 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';

要使用代理帐户,请使用其名称和密码连接到服务器

$> mysql -u proxy_user -p
Enter password: (enter proxy_user password here)

身份验证成功后,服务器会发现 proxy_user 具有 proxied_userPROXY 权限,并且会话将继续进行,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