Documentation Home
MySQL 8.4 Reference Manual
Related Documentation Download this Manual
PDF (US Ltr) - 39.8Mb
PDF (A4) - 39.9Mb
Man Pages (TGZ) - 257.9Kb
Man Pages (Zip) - 364.9Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


8.2.19 代理用户

MySQL 服务器使用身份验证插件来验证客户端连接。验证插件可能会要求连接的外部用户被视为另一个用户,以便为权限检查目的使用该用户。这使得外部用户可以作为第二个用户的代理,以假设第二个用户的权限:

  • 外部用户被称为代理用户(可以模拟或被认为是另一个用户)。

  • 第二个用户被称为被代理用户(可以被代理用户模拟或被认为是另一个用户)。

本节描述了代理用户机制的工作原理。关于身份验证插件的总体信息,请见第8.2.17节,“可插拔身份验证”。关于特定插件的信息,请见第8.4.1节,“身份验证插件”。关于编写支持代理用户的身份验证插件,请见在身份验证插件中实现代理用户支持

Note

代理用户的一个管理益处是DBA可以设置一个帐户的权限,然后启用多个代理用户拥有这些权限,而不需要为每个用户分配权限。DBA可能会发现,角色提供了将用户映射到特定权限集的方式。每个用户可以被授予单个角色,以便授予适当的权限。请见第8.2.10节,“使用角色”

为了使代理用户在给定的身份验证插件中生效,必须满足以下条件:

  • 代理必须被支持,通过插件本身或MySQL服务器在插件的 behalf。后一种情况,服务器支持可能需要被启用;请参见服务器支持代理用户映射

  • 外部代理用户的账户必须被设置为由插件进行身份验证。使用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将用户名返回为employee,基于'my_auth_string'的内容或外部身份验证系统的内容。名称employee不同于employee_ext,因此返回employee作为请求服务器将employee_ext外部用户视为employee本地用户,以便在权限检查时进行。

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

服务器验证 employee_ext 用户是否可以为 employee 用户执行代理身份验证,方法是检查 employee_ext 用户是否拥有 PROXY 权限,以便访问 employee 用户。如果没有授予权限,会出现错误。否则,employee_ext 将继承 employee 用户的权限。服务器将在客户端会话中执行的语句,检查它们是否符合 employee 用户的权限。在这种情况下,employee_ext 可以访问 employees 数据库中的表。

被代理的账户 employee 使用 mysql_no_login 认证插件来防止客户端直接使用账户登录。 (假设插件已经安装。有关安装的指南,请见第8.4.1.9节,“No-Login Pluggable Authentication”。) 对于保护被代理账户的直接使用的其他方法,请见Preventing Direct Login to Proxied Accounts

当代理身份验证发生时,可以使用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特权来使外部用户连接到另一个用户并拥有该用户的权限。要授予此特权,请使用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 OPTION权限的用户授予proxied_user

  • 或由proxied_user自己授予:The value of USER() must exactly match CURRENT_USER() and proxied_user, for both the user name and host name parts of the account name.

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

服务器找不到 myuser 定义为 MySQL 用户,但因为存在一个空白用户账户(''@''),该账户匹配客户端用户名和主机名,服务器对客户端进行身份验证。服务器调用 ldap_auth 身份验证插件,并将 myusermyuser_password 传递给它作为用户名和密码。

如果 ldap_auth 插件在 LDAP 目录中找到 myuser_password 不是 myuser 的正确密码,身份验证失败,服务器拒绝连接。

如果密码正确,ldap_auth 插件找到 myuser 是开发者,它返回用户名 developer 给 MySQL 服务器,而不是 myuser。返回的用户名不同于客户端用户名 myuser,提示服务器将 myuser 视为代理用户。服务器验证 ''@'' 可以作为 developer 进行身份验证(因为 ''@'' 拥有PROXY特权),并接受连接。会话继续使用 myuser 拥有代理用户的权限。 (这些权限应该由 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账户,这些代理账户仍然会被使用(参见Preventing Direct Login to Proxied Accounts)。

如果你想创建默认代理用户,请检查其他存在的match any user账户,因为它们可以防止默认代理用户工作正常。

在前面的讨论中,默认代理用户账户的主机部分是'',这将匹配任何主机。如果你设置了默认代理用户,请确保也检查是否存在同名账户,但主机部分是'%',因为'%'也可以匹配任何主机,但根据服务器内部排序规则,它们的优先级高于''(参见Section 8.2.6, “Access Control, Stage 1: Connection Verification”)。

假设有一台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匿名用户连接。

  • 使用命名默认账户,而不是匿名默认账户。关于该技术的示例,请参阅第8.4.1.6节,“Windows Pluggable Authentication”

  • 创建多个代理用户,一個用于本地连接,一個用于everything else(远程连接)。这在本地用户和远程用户应该有不同的权限时非常有用。

    创建代理用户:

    -- 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_passwordsha256_password。如果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具有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 进行身份验证。插件可能将这个外部用户 ID(或其 UTF-8 字节的前 512 字节)返回到服务器使用external_user只读会话变量。如果插件不设置这个变量,它的值是NULL