6.3 Mysql用户账号 管理

参考官方文档:

https://dev.mysql.com/doc/refman/5.7/en/user-account-management.html

本节介绍如何为MySQL服务器的客户端设置帐户。 它讨论了以下主题:

  • MySQL中使用的帐户名称和密码的含义以及如何与您的操作系统使用的名称和密码进行比较
  • 如何设置新帐户并删除现有帐户
  • 如何更改密码
  • 安全使用密码准则

6.3.1 用户名和密码

MySQL将帐户存储在mysql系统数据库的user表中。 帐户是根据用户名称和用户可以连接到服务器的客户端主机或主机定义的。

该帐户也可能有密码。 MySQL支持身份验证插件,因此有可能使用某种外部身份验证方法进行身份验证。

MySQL和您的操作系统使用用户名和密码的方式有几点区别:

  • 用于身份验证的MySQL用户名与Windows或Unix使用的用户名(登录名)无关。 在Unix上,大多数MySQL客户端默认尝试使用当前Unix用户名作为MySQL用户名登录,但这仅仅是为了方便。 可以轻松地覆盖默认值,因为客户端程序允许使用-u或–user选项指定任何用户名。 这意味着任何人都可以尝试使用任何用户名连接到服务器,因此除非所有MySQL帐户都有密码,否则无法以任何方式保护数据库安全。 任何为没有密码的帐户指定用户名的人都可以成功连接到服务器。
  • MySQL用户名最长可达32个字符,操作系统用户名可以具有不同的最大长度。 例如,Unix用户名通常限制为八个字符。

注意:

MySQL用户名长度的限制是在MySQL服务器和客户端中进行硬编码的,试图通过修改mysql数据库中表的定义来绕过它是行不通的。

除非通过第4.4.7节“mysql_upgrade – 检查和升级MySQL表”中介绍的过程,否则绝不应以任何方式更改mysql数据库中表的结构。 试图以任何其他方式重新定义MySQL的系统表会导致未定义(和不支持!)的行为。 服务器可以自由忽略由于此类修改而变得格式错误的行。

  • 为了验证使用MySQL本地认证(由mysql_native_password认证插件实现)的帐户的客户端连接,服务器使用存储在用户表中的密码。 这些密码与用于登录到操作系统的密码不同。 用于登录Windows或Unix机器的”external”密码与用于访问该机器上MySQL服务器的密码之间没有必要的联系。

如果服务器使用其他插件对客户端进行身份验证,则插件实现的身份验证方法可能会或可能不会使用存储在用户表中的密码。 在这种情况下,可能还会使用外部密码对MySQL服务器进行身份验证。

  • 存储在用户表中的密码使用特定于插件的算法进行加密。
  • 如果用户名和密码只包含ASCII字符,则无论字符集设置如何,都可以连接到服务器。 要在用户名或密码包含非ASCII字符时进行连接,客户端应使用MYSQL_SET_CHARSET_NAME选项和相应的字符集名称作为参数调用mysql_options()C API函数。 这会导致使用指定的字符集进行身份验证。 否则,认证将失败,除非服务器默认字符集与认证默认值中的编码相同。

标准的MySQL客户端程序支持一个–default-character-set选项,导致mysql_options()按刚才描述的方式被调用。 此外,支持字符集自动检测,如第10.4节“连接字符集和归类”中所述。 对于使用不基于C API的连接器的程序,连接器可能会提供与mysql_options()等效的替代方法。 检查连接器文档。

要使用命令行客户端连接到MySQL服务器,请根据需要为要使用的帐户指定用户名和密码选项:

shell> mysql –user=finley –password db_name

如果您更喜欢短期选项,该命令如下所示:

shell> mysql -u finley -p db_name

如果您在命令行中使用–password或-p选项(如刚才所示)省略密码值,则客户端会提示输入密码值。 或者,可以在命令行中指定密码:

shell> mysql –user=finley –password=password db_name

shell> mysql -u finley -ppassword db_name

如果您在命令行中使用–password或-p选项(如刚才所示)省略密码值,则客户端会提示输入密码值。 或者,可以在命令行中指定密码:

shell> mysql –user=finley –password=password db_name

shell> mysql -u finley -ppassword db_name

如果使用-p选项,则-p和以下密码值之间不能有空格。

6.3.2 增加用户账号

你可以以下面两种方式创建MySQL账户:

  • 通过使用用于创建帐户和建立其权限的帐户管理语句,如CREATE USER和GRANT。 这些语句会导致服务器对基础授权表进行适当的修改。
  • 通过直接使用诸如INSERT,UPDATE或DELETE之类的语句来操作MySQL授权表。

首选的方法是使用帐户管理语句,因为它们比直接操作授权表更简洁,更不容易出错。 所有这些陈述在第13.7.1节“账户管理陈述”中都有描述。 不鼓励直接对grant表修改,这里不作介绍。 服务器可以自由忽略由于此类修改而变得格式错误的行。

创建账户的另一个选择是使用GUI工具MySQL Workbench。 此外,还有几个第三方程序提供了MySQL帐户管理功能。 phpMyAdmin就是这样一个程序。

以下示例显示如何使用mysql客户端程序设置新帐户。 这些示例假定已根据第2.10.4节“确保初始MySQL帐户的安全”中所述的默认设置设置权限。 要进行更改,您必须以具有CREATE USER权限的MySQL root用户身份连接到MySQL服务器。

首先,使用mysql程序作为MySQL root用户连接到服务器:

shell> mysql –user=root mysql

如果您已为root帐户分配密码,则还必须提供–password或-p选项。

以root身份连接到服务器后,您可以添加新帐户。 以下示例使用CREATE USER和GRANT语句来设置四个帐户:

mysql> CREATE USER ‘finley’@’localhost’ IDENTIFIED BY ‘password‘;

mysql> GRANT ALL PRIVILEGES ON *.* TO ‘finley’@’localhost’ WITH GRANT OPTION;

mysql> CREATE USER ‘finley’@’%’ IDENTIFIED BY ‘password‘;

mysql> GRANT ALL PRIVILEGES ON *.* TO ‘finley’@’%’ WITH GRANT OPTION;

mysql> CREATE USER ‘admin’@’localhost’ IDENTIFIED BY ‘password‘;

mysql> GRANT RELOAD,PROCESS ON *.* TO ‘admin’@’localhost’;

mysql> CREATE USER ‘dummy’@’localhost’;

这些语句创建的帐户具有以下属性:

  • 两个账户都有finley的用户名。 两者都是拥有完全权限的超级用户帐户。 ‘finley’@’localhost’帐户只能在从本地主机连接时使用。 ‘finley’@’%’帐户为主机部分使用’%’通配符,因此可用于从任何主机连接。

如果有localhost的匿名用户帐户,那么’finley’@’localhost’帐户是必需的。 如果没有’finley’@’localhost’帐户,那么当从本地主机连接finley并将finley视为匿名用户时,匿名用户帐户优先。 原因在于匿名用户帐户具有比’finley’@’%’帐户更具体的主机列值,因此在用户表排序顺序中更早。

  • ‘admin’@’localhost’帐户只能由admin用于从本地主机进行连接。 它被授予RELOAD和PROCESS管理权限。 这些特权使admin 用户可以执行mysqladmin reload,mysqladmin refresh和mysqladmin flush-xxx命令以及mysqladmin processlist。 没有权限访问任何数据库。 您可以使用GRANT语句添加这些权限。
  • ‘dummy’@’localhost’帐户没有密码(这是不安全的,不推荐)。 该帐户只能用于从本地主机进行连接。 没有授予特权。 假定您将使用GRANT语句为该帐户授予特定的权限。

6.3.3 移除 用户账户

要删除帐户,请使用第13.7.1.3节“DROP USER语法”中描述的DROP USER语句。 例如:

mysql> DROP USER ‘jeffrey’@’localhost’;

6.3.4 保留用户账户

MySQL安装过程的一部分是数据目录初始化(参见第2.10.1.1节“使用mysqld手动初始化数据目录”)。 在数据目录初始化期间,MySQL创建应被视为保留的用户帐户:

  • ‘root’@’localhost:用于管理目的。 此帐户拥有所有权限并可执行任何操作。

严格来讲,这个帐户名不会被保留,因为某些安装将root帐户重命名为其他内容,以避免暴露具有众所周知名称的高度特权帐户。

  • ‘mysql.sys’@’localhost’:用作sys模式对象的DEFINER。 使用mysql.sys帐户可避免DBA重命名或删除root帐户时发生的问题。 此帐户已锁定,因此无法用于客户端连接。
  • ‘mysql.session’@’localhost’:由插件内部使用来访问服务器。 此帐户已锁定,因此无法用于客户端连接。

6.3.5 设置账户资源限制

限制客户端使用MySQL服务器资源的一种方法是将全局max_user_connections系统变量设置为非零值。 这限制了任何给定帐户可以进行的同时连接的数量,但对连接后客户端可以执行的操作没有限制。 另外,设置max_user_connections不支持管理单个帐户。 这两种类型的控制都是MySQL管理员感兴趣的。

为了解决这些问题,MySQL允许单个账户使用这些服务器资源的限制:

  • 一个帐户每小时可以发出的查询次数
  • 一个帐户每小时可以发布的update次数
  • 帐户每小时可以连接到服务器的次数
  • 一个帐户同时连接到服务器的数量

除非从查询缓存中提供结果,否则客户端可以发出的任何语句都会与查询限制相关。

–一般情况下,很少会对账户执行的语句次数做显示

此上下文中的“account”对应于mysql.user表中的一行。 也就是说,根据适用于连接的user表行中的User和Host值来评估连接。 例如,一个帐户’usera’@’%.example.com’对应于用户表中具有usera和%.http://example.com的用户和主机值的行,以允许usera 从该example..com 域中的任何主机进行连接。 在这种情况下,服务器将此行中的资源限制集中应用于来自http://example.com域中任何主机的usera的所有连接,因为所有此类连接都使用相同的帐户。

在MySQL 5.0之前,会根据用户连接的实际主机评估一个“帐户”。 可以通过使用–old-style-user-limits选项启动服务器来选择这种较旧的记帐方法。 在这种情况下,如果usera同时从http://host1.example.com和http://host2.example.com连接,则服务器将帐户资源限制分别应用于每个连接。 如果usera再次从http://host1.example.com连接,则服务器将该连接的限制与来自该主机的现有连接一起应用。

–之前是分主机的

要在帐户创建时为帐户建立资源限制,请使用CREATE USER语句。 要修改现有帐户的限制,请使用ALTER USER。 提供一个将每个资源命名为受限的WITH子句。 每个限制的默认值为零(无限制)。 例如,要创建一个可以访问客户数据库的新帐户,但是是受限的方式,请发出以下语句:

mysql> CREATE USER ‘francis’@’localhost’ IDENTIFIED BY ‘frank’

WITH MAX_QUERIES_PER_HOUR 20

MAX_UPDATES_PER_HOUR 10

MAX_CONNECTIONS_PER_HOUR 5

MAX_USER_CONNECTIONS 2;

限制类型不需要全部在WITH子句中命名,但是可以以任何顺序存在。 每个每小时限制的值应该是代表每小时计数的整数。 对于MAX_USER_CONNECTIONS,限制是表示帐户同时连接的最大数量的整数。 如果此限制设置为零,则全局max_user_connections系统变量值将确定同时连接的数量。 如果max_user_connections也为零,则该帐户没有限制。

要修改现有帐户的限制,请使用ALTER USER语句。 以下语句将francis的查询限制更改为100:

mysql> ALTER USER ‘francis’@’localhost’ WITH MAX_QUERIES_PER_HOUR 100;

该声明仅修改指定的限制值,并使帐户保持不变。

要删除限制,请将其值设置为零。 例如,要删除francis每小时可连接多少次的限制,请使用以下语句:

mysql> ALTER USER ‘francis’@’localhost’ WITH MAX_CONNECTIONS_PER_HOUR 0;

如前所述,帐户的同时连接限制取决于MAX_USER_CONNECTIONS限制和max_user_connections系统变量。 假设全局max_user_connections值为10,并且三个帐户具有如下指定的单个资源限制:

ALTER USER ‘user1’@’localhost’ WITH MAX_USER_CONNECTIONS 0;

ALTER USER ‘user2’@’localhost’ WITH MAX_USER_CONNECTIONS 5;

ALTER USER ‘user3’@’localhost’ WITH MAX_USER_CONNECTIONS 20;

user1的连接限制为10(全局max_user_connections值),因为它的MAX_USER_CONNECTIONS限制为零。 user2和user3分别具有5和20的连接限制,因为它们具有非零的MAX_USER_CONNECTIONS限制。

服务器存储帐户对应的用户表格行中的资源限额。 max_questions,max_updates和max_connections列存储每小时限制,max_user_connections列存储MAX_USER_CONNECTIONS限制。

资源使用计数发生在任何帐户对其任何资源的使用都有非零限制的情况下。

在服务器运行时,它会计算每个帐户使用资源的次数。 如果某个帐户在最近一小时内达到其连接数限制,则该服务器会拒绝该帐户的其他连接,直到该小时数到达。 同样,如果帐户达到查询或更新数量的限制,服务器会拒绝进一步的查询或更新,直到小时结束。 在所有这些情况下,服务器都会发出适当的错误消息。

资源统计发生在每个帐户,而不是每个客户端。 例如,如果您的帐户具有50的查询限制,则无法将两个客户端连接同时连接到服务器,将限制增加到100。 在两个连接上发布的查询都一起计算。

当前每小时资源使用计数可以针对所有帐户全局重置,也可以针对给定帐户单独重置:

  • 要将所有帐户的当前计数重置为零,请发出FLUSH USER_RESOURCES语句。 计数还可以通过重新加载授权表来重置(例如,使用FLUSH PRIVILEGES语句或mysqladmin重新加载命令)。
  • 通过再次设置任何限制,个人账户的计数可以重置为零。 指定一个等于当前分配给帐户的值的限制值。

服务器启动时,所有计数都从零开始。

对于MAX_USER_CONNECTIONS限制,如果帐户当前打开了允许的最大连接数,则会出现边缘情况:如果服务器尚未完全处理连接,则连接快速发生的断开连接可能会导致错误(ER_TOO_MANY_USER_CONNECTIONS或ER_USER_LIMIT_REACHED) 。 当服务器完成断开连接处理时,将再次允许另一个连接。

6.3.6 分配账户密码

连接到MySQL服务器的客户端所需的凭据可以包含密码。 本节介绍如何为MySQL帐户分配密码。

MySQL将凭证存储在mysql系统数据库的user表中。 分配或修改密码的操作只允许具有CREATE USER特权的用户或者mysql数据库的特权(INSERT特权创建新帐户,UPDATE特权修改现有帐户)的用户使用。 如果启用了read_only系统变量,则使用帐户修改语句(如CREATE USER或ALTER USER)还需要SUPER权限。

此处的讨论仅汇总了最常用的密码分配语句的语法。

MySQL使用插件来执行客户端身份验证; 参见第6.3.9节“可插入认证”。 在密码分配语句中,与帐户关联的认证插件会执行指定的明文密码所需的任何哈希。 这使MySQL能够在将密码存储到mysql.user表之前模糊密码。 对于这里描述的语句,MySQL自动哈希指定的密码。 还有CREATE USER和ALTER USER的语法,允许字面值指定哈希值。 有关详细信息,请参阅这些语句的说明。

要在创建新帐户时分配密码,请使用CREATE USER并包含一个IDENTIFIED BY子句:

CREATE USER ‘jeffrey’@’localhost’ IDENTIFIED BY ‘password‘;

要为现有帐户分配或更改密码,请使用带有IDENTIFIED BY子句的ALTER USER语句:

ALTER USER ‘jeffrey’@’localhost’ IDENTIFIED BY ‘password‘;

如果您没有以匿名用户身份进行连接,则可以更改自己的密码,而不必直接命名自己的帐户:

ALTER USER USER() IDENTIFIED BY ‘password‘;

要从命令行更改帐户密码,请使用mysqladmin命令:

mysqladmin -u user_name -h host_name password “password

此命令设置密码的帐户是具有与用户列中的user_name匹配的mysql.user表行,以及您在Host列中连接的客户端主机的帐户。

6.3.7 密码管理

MySQL使数据库管理员能够手动过期帐户密码,并建立自动密码过期策略。 可以在全球范围内建立到期保单政策,也可以按照每个帐户进行。 这些功能适用于使用MySQL内置身份验证插件(mysql_native_password或sha256_password)的帐户。 对于使用对外部凭证系统执行验证的插件的帐户,密码到期也必须在外部处理。

要手动过期帐户密码,请使用ALTER USER语句:

ALTER USER ‘jeffrey’@’localhost’ PASSWORD EXPIRE;

此操作将在相应的mysql.user表行中标记过期的密码。

根据策略的密码过期是自动的,并且基于密码的年龄,对于给定的帐户,密码的年龄根据其最近一次密码更改的日期和时间进行评估。 mysql.user表指示每个帐户的密码上次更改时间,如果服务器的年龄大于其允许的生存期,服务器会自动将密码视为在客户端连接时过期。 这适用于没有明确的手动密码到期的情况。

要全局建立自动密码到期策略,请使用default_password_lifetime系统变量。 其默认值为0,禁用自动密码过期。 如果default_password_lifetime的值为正整数N,则表示允许的密码生存期,因此密码必须每N天更改一次。

注意:

在5.7.11之前,默认的default_password_lifetime值是360(密码必须每年大约更改一次)。 对于此类版本,请注意,如果不对default_password_lifetime变量或个人用户帐户进行更改,则每个用户密码将在360天后过期,并且帐户开始以受限制模式运行。 使用该帐户连接到服务器的客户端会收到错误消息,指出密码必须更改:错误1820(HY000): You must reset your password using ALTER USER statement before executing this statement.

但是,对于自动连接到服务器的客户端(如使用脚本建立的连接),很容易错过。 为避免由于密码过期而导致此类客户端突然停止工作,请确保为这些客户端更改密码到期设置,如下所示:

ALTER USER ‘script’@’localhost’ PASSWORD EXPIRE NEVER

例如:

要建立一个全局策略,密码的使用期限大约为六个月,请在服务器my.cnf文件中使用以下行启动服务器:

[mysqld]

default_password_lifetime=180

要建立全局策略以使密码永不过期,请将default_password_lifetime设置为0:

SET GLOBAL default_password_lifetime = 180;

SET GLOBAL default_password_lifetime = 0;

要为各个帐户建立密码到期策略,请使用CREATE USER和ALTER USER语句的PASSWORD EXPIRE选项。

账户特定的到期语句示例:

  • 要求每90天更换密码:

CREATE USER ‘jeffrey’@’localhost’ PASSWORD EXPIRE INTERVAL 90 DAY;

ALTER USER ‘jeffrey’@’localhost’ PASSWORD EXPIRE INTERVAL 90 DAY;

  • 关闭密码过期

CREATE USER ‘jeffrey’@’localhost’ PASSWORD EXPIRE NEVER;

ALTER USER ‘jeffrey’@’localhost’ PASSWORD EXPIRE NEVER;

  • 遵循全局到期策略:

CREATE USER ‘jeffrey’@’localhost’ PASSWORD EXPIRE DEFAULT;

ALTER USER ‘jeffrey’@’localhost’ PASSWORD EXPIRE DEFAULT;

当客户端成功连接时,服务器将确定帐户密码是否已过期:

  • 服务器检查密码是否已手动过期。
  • 否则,服务器根据自动密码过期策略检查密码年龄是否大于其允许的生存期。 如果是这样,服务器认为密码已过期。

客户端重置密码后,服务器将恢复会话的正常访问权限,并恢复使用该帐户的后续连接。 管理用户也可以重置帐户密码,但该帐户的任何现有受限制会话仍然受到限制。 在成功执行语句之前,使用该帐户的客户端必须断开连接并重新连接。

注意:

可以通过将密码设置为当前值来“重置”密码。 作为一个良好的政策,最好选择一个不同的密码。

6.3.9 Pluggable 身份认证

当客户端连接到MySQL服务器时,服务器使用客户端和客户端主机提供的用户名从mysql.user系统表中选择适当的帐户行。 然后,服务器对客户端进行身份验证,从帐户行确定哪个身份验证插件适用于客户端:

  • 如果服务器找不到插件,则会发生错误并拒绝连接尝试。
  • 否则,服务器调用该插件来认证用户,插件向服务器返回一个状态,指示用户是否提供了正确的密码并被允许连接。

可插入的身份验证启用了以下重要功能:

  • 选择身份认证的方式。可插入的身份验证使DBA可以轻松选择和更改用于各个MySQL帐户的身份验证方法。
  • 外本身份认证。可插入身份验证使客户端可以使用适用于存储其他凭据的身份验证方法的证书连接到MySQL服务器,而不是在mysql.user系统表中。 例如,可以创建插件以使用外部身份验证方法,例如PAM,Windows登录ID,LDAP或Kerberos。
  • 代理用户:如果允许用户连接,则认证插件可以向服务器返回与连接用户的名称不同的用户名,以指示连接用户是另一用户(代理用户)的代理。 当连接持续时,为了访问控制,代理用户被视为具有代理用户的权限。 实际上,一个用户冒充另一个用户。

注意:

如果使用–skip-grant-tables选项启动服务器,则即使加载身份验证插件也不会使用,因为服务器不执行客户端身份验证并允许任何客户端进行连接。 由于这是不安全的,因此您可能希望将–skip-grant-tables与–skip-networking一起使用,以防止远程客户端连接。

可用的身份认证插件

MySQL 5.7提供了这些认证插件:

  • 执行本机认证的插件; 也就是说,基于在MySQL中引入可插入认证之前使用的密码哈希方法的认证。 mysql_native_password插件基于本机密码哈希方法实现认证。 mysql_old_password插件基于较旧的(4.1之前的)密码哈希方法实现本地认证(并且在MySQL 5.7.5中已被弃用和删除)。
  • 使用SHA-256密码哈希执行验证的插件。 这是比可用的本机认证更强的加密。
  • 一个客户端插件,它将密码发送到服务器而不进行哈希或加密。 该插件与服务器端插件一起使用,这些插件需要完全按照客户端用户提供的密码访问。
  • 使用PAM(可插入认证模块)执行外部认证的插件,使MySQL服务器能够使用PAM来认证MySQL用户。 这个插件也支持代理用户。
  • 一个在Windows上执行外部验证的插件,使MySQL服务器能够使用本机Windows服务来验证客户端连接。 登录到Windows的用户可以根据其环境中的信息从MySQL客户端程序连接到服务器,无需指定额外的密码。 这个插件也支持代理用户。
  • 使用LDAP(轻量目录访问协议)执行身份验证的插件,通过访问目录服务(如X.500)对MySQL用户进行身份验证。 这些插件也支持代理用户。
  • 阻止所有客户端连接到使用它的任何帐户的插件。 此插件的使用案例包括代理帐户,绝对不应允许直接登录,只能通过代理帐户和必须能执行存储程序和视图的权限帐户进行访问,而不会将这些特权暴露给普通用户。
  • 验证通过Unix套接字文件从本地主机连接的客户端的插件。
  • 测试插件,用于检查帐户凭证并将成功或失败记录到服务器错误日志中。 此插件旨在用于测试和开发目的,并作为如何编写身份验证插件的示例。

注意:

第三方连接器开发人员应该阅读该部分,以确定连接器可以利用可插入身份验证功能的程度以及采取哪些步骤以变得更加合规。

身份插件的使用

通常,可插入认证在服务器和客户端使用一对相应的插件,因此您使用如下所示的给定认证方法:

  • 如有必要,请安装插件库或包含相应插件的库。 在服务器主机上,安装包含服务器端插件的库,以便服务器可以使用它来验证客户端连接。 同样,在每台客户端主机上,安装包含客户端插件的库供客户端程序使用。 无需安装内置的身份验证插件。
  • 对于您创建的每个MySQL帐户,请指定相应的服务器端插件用于身份验证。 如果帐户要使用默认身份验证插件,则帐户创建语句不需要明确指定插件。 default_authentication_plugin系统变量配置默认的身份验证插件。
  • 当客户端连接时,服务器端插件会告诉客户端程序使用哪个客户端插件进行身份验证。

在帐户使用服务器和客户端程序默认的身份验证方法的情况下,服务器无需与客户端通信使用哪个客户端插件,并且可以在客户端/服务器协商中进行往返避免。 对于使用本机MySQL身份验证的帐户是True。

对于标准的MySQL客户端,例如mysql和mysqladmin,可以在命令行中指定–default-auth = plugin_name选项作为关于该程序可以使用哪个客户端插件的提示,尽管服务器会覆盖 ,如果与用户帐户关联的服务器端插件需要不同的客户端插件。

如果客户端程序未找到客户端插件库文件,请指定–plugin-dir = dir_name选项以指示插件库目录位置。

6.3.10 代理用户

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

  • 外部用户是“代理用户”(可以扮演或被称为另一个用户的用户)。
  • 第二个用户是“代理用户”(一个用户,其身份和权限可以由代理用户承担)。

代理用户支持的需求

为了对给定的认证插件进行代理,必须满足以下条件:

  • 代理必须被支持,或者由插件本身支持,或者由MySQL服务器代在插件行为上支持。 在后一种情况下,可能需要明确启用服务器支持;
  • 代理用户帐户必须设置为由插件进行认证。 使用CREATE USER语句将帐户与身份验证插件关联,或使用ALTER USER更改其插件。
  • 必须创建代理用户帐户并授予代理用户所需的权限。 为此使用CREATE USER和GRANT语句。
  • 代理用户帐户必须具有代理帐户的PROXY特权。 为此,使用GRANT语句。
  • 对于连接到代理帐户的客户端将被视为代理用户,身份验证插件必须返回与客户端用户名不同的用户名,以指示代理帐户的用户名,代理用户承担定义的权限。

或者,对于由服务器提供代理映射的插件,代理用户由代理用户持有的PROXY权限来确定。

代理机制只允许将客户端用户名映射到代理用户名。 没有规定映射主机名称。 当连接客户端与代理帐户匹配时,服务器将尝试使用身份验证插件返回的用户名和代理帐户的主机名来查找代理帐户的匹配项。

考虑如下账户定义:

— create proxy account

CREATE USER ’employee_ext’@’localhost’ IDENTIFIED WITH my_auth_plugin AS ‘my_auth_string’;

CREATE USER ’employee’@’localhost’ IDENTIFIED BY ’employee_pass’;

GRANT ALL ON employees.* TO ’employee’@’localhost’;

GRANT PROXY ON ’employee’@’localhost’ TO ’employee_ext’@’localhost’;

当客户端以本地主机的employee_ext连接时,MySQL使用名为my_auth_plugin的插件来执行身份验证。 假设my_auth_plugin根据’my_auth_string’的内容并可能通过咨询某个外部认证系统,将 employee 的用户名返回给服务器。 名称 employee 与employee_ext不同,因此返回的员工将作为服务器的请求,将employee_ext客户端作为 employee 本地用户进行特权检查。

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

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

代理发生时,USER()和CURRENT_USER()函数可用于查看连接用户(代理用户)与在当前会话期间应用权限的帐户(被代理用户)之间的差异。 对于刚才描述的例子,这些函数返回这些值:

mysql> SELECT USER(), CURRENT_USER();

在创建代理用户帐户的CREATE USER语句中,为认证插件命名的IDENTIFIED WITH子句可选地跟随一个AS’auth_string’子句,该子句指定服务器在用户连接时传递给插件的字符串。 如果存在,该字符串将提供帮助插件确定如何将外部客户端用户名映射到代理用户名的信息。 是否需要AS子句取决于每个插件。 如果是这样,认证字符串的格式取决于插件打算如何使用它。 有关它接受的验证字符串值的信息,请查阅给定插件的文档。

授予代理权限

需要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 GRANT和REVOKE语法扩展与平常一样工作。 例如:

GRANT PROXY ON ‘a’ TO ‘b’, ‘c’, ‘d’;

GRANT PROXY ON ‘a’ TO ‘d’ WITH GRANT OPTION;

GRANT PROXY ON ‘a’ TO ”@”;

REVOKE PROXY ON ‘a’ FROM ‘b’, ‘c’, ‘d’;

在这些情况下可以授予PROXY特权:

  • 由具有GRANT PROXY … WITH GRANT OPTION 的 proxied_user的用户使用。
  • 通过自身的proxied_user:USER()的值必须与CURRENT_USER()和proxied_user完全匹配,用于帐户名称的用户名和主机名部分。

在MySQL安装过程中创建的初始root帐户具有PROXY … WITH GRANT OPTION特权用于“@”,也就是说,对于所有用户和所有主机。 这使得root可以设置代理用户,也可以委托其他账户设置代理用户的权限。 例如,root可以这样做:

CREATE USER ‘admin’@’localhost’ IDENTIFIED BY ‘test’;

GRANT PROXY ON ”@” TO ‘admin’@’localhost’ WITH GRANT OPTION;

这些语句创建一个可以管理所有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_pass’;

CREATE USER ‘manager’@’localhost’ IDENTIFIED BY ‘manager_pass’; — grant PROXY privilege to default proxy account for proxied accounts GRANT PROXY ON ‘manager’@’localhost’ TO ”@”;

GRANT PROXY ON ‘developer’@’localhost’ TO ”@”;

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

shell> mysql –user=myuser –password …

Enter password: myuser_pass

服务器不会将myuser定义为MySQL用户。 但由于存在与客户端用户名和主机名相匹配的空白用户帐户(”@”),服务器会根据该帐户对客户端进行身份验证:服务器调用ldap_auth身份验证插件并将myuser和myuser_pass传递给它 用户名和密码。

如果ldap_auth插件在LDAP目录中发现myuser_pass不是myuser的正确密码,则认证失败,服务器拒绝连接。

如果密码正确,并且ldap_auth发现myuser是开发人员,它将用户名 developer 返回给MySQL服务器,而不是myuser。 将不同于myuser的客户端用户名的用户名返回给服务器,以便将myuser作为代理处理。 服务器验证“@”可以作为 developer 进行身份验证(因为该帐户具有PROXY权限来执行此操作)并接受连接。 会话继续与myuser有 developer权限。 (这些权限应由DBA使用GRANT语句设置,未显示。)USER()和CURRENT_USER()函数返回这些值:

mysql> SELECT USER(), CURRENT_USER();

如果插件在LDAP目录中发现myuser是 manager,它将返回 manager作为用户名,并且会话继续以具有 manager权限的myuser进行。

mysql> SELECT USER(), CURRENT_USER(); +——————+——————-+ | USER() | CURRENT_USER() | +——————+——————-+ | myuser@localhost | manager@localhost | +——————+——————-+

为了简单起见,外部认证不能是多级的:在前面的例子中, developer 的凭证和manager 的凭证都没有考虑到。 但是,如果客户端尝试直接作为developer 或manager 帐户进行连接和身份验证,仍然会使用它们,这就是为什么应为这些帐户分配密码的原因。

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

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

在前面的讨论中,默认的代理用户帐户在主机部分有”,它与任何主机都匹配。 如果您设置了默认的代理用户,请注意同时检查非代理帐户是否存在相同的用户部分和主机部分中的’%’,因为’%’也与任何主机相匹配,但是由规则优先于” 服务器

假设MySQL安装包含这两个帐户:

— create default proxy account

CREATE USER ”@” IDENTIFIED WITH some_plugin AS ‘some_auth_string’;

— create anonymous account

CREATE USER ”@’%’ IDENTIFIED BY ‘some_password’;

第一个帐户(”@”)旨在用作默认代理用户,用于验证没有以其他方式匹配更具体帐户的用户的连接。 第二个帐户(”@’%’)是一个匿名用户帐户,可能已经创建了该帐户,例如,为了让用户没有自己的帐户进行匿名连接。

两个帐户都有相同的用户部分(”),它与任何用户匹配。 并且每个帐户都有一个与任何主机匹配的主机部分。 尽管如此,由于匹配规则会在”之前对主机进行排序,因此连接尝试的帐户匹配优先。 对于不符合任何特定帐户的帐户,服务器将尝试使用”@’%’(匿名用户)而不是”@”(默认代理用户)对其进行身份验证。 结果是从不使用默认代理帐户。

要避免此问题,请使用以下策略之一:

  • 删除匿名帐户,以便它不会与默认代理用户冲突。 无论如何,这可能是一个好主意,如果你想将每个连接与一个指定的用户相关联。
  • 使用与匿名用户相匹配的更具体的默认代理用户。 例如,要仅允许本地主机代理连接,请使用”@ localhost’:

CREATE USER ”@’localhost’ IDENTIFIED WITH some_plugin AS ‘some_auth_string’;

另外,修改任何GRANT PROXY语句来命名”@’localhost’而不是’@”作为代理用户。

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

  • 创建多个代理用户,一个用于本地连接,另一个用于“其他”(远程连接)。 当本地用户应该具有远程用户的不同权限时,这可能很有用。

创建代理用户:

— 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’;

为每个代理用户授予相应代理用户的代理权限:

GRANT PROXY ON ‘developer’@’localhost’ TO ”@’localhost’;

GRANT PROXY ON ‘developer’@’%’ TO ”@’%’;

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

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

服务器支持代理用户映射

一些认证插件为自己实现代理用户映射。 对于某些人来说,MySQL服务器本身可以根据授予的代理权限映射代理用户。 如果启用了check_proxy_users系统变量,则服务器将对请求它的任何验证插件执行代理用户映射:

  • 默认情况下,check_proxy_users被禁用,所以即使对请求它的认证插件,服务器也不执行代理用户映射。
  • 启用check_proxy_users后,可能还需要启用特定于插件的系统变量以利用服务器代理用户映射支持:
    • 对于mysql_native_password插件,请启用mysql_native_password_proxy_users。
    • 对于sha256_password插件,启用sha256_password_proxy_users。

服务器执行的代理用户映射受以下限制:

  • 即使授予相关的PROXY特权,服务器也不会向匿名用户代理或从匿名用户代理。
  • 当一个帐户被授予多个代理帐户的代理权限时,服务器代理用户映射不确定。 因此,不鼓励为多个代理帐户授予单个帐户代理权限。

代理用户系统变量

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

  • proxy_user:如果不使用代理,则此值为NULL。 否则,它表示代理用户帐户。 例如,如果客户端通过”@” 代理帐户进行认证,则该变量设置如下:

mysql> SELECT @@proxy_user;

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

6.3.11 用户账户锁定

从版本5.7.6开始,MySQL支持使用ACCOUNT LOCK和ACCOUNT UNLOCK子句为CREATE USER和ALTER USER语句锁定和解锁用户帐户:

  • 与CREATE USER一起使用时,这些子句指定新帐户的初始锁定状态。 在没有任何一个子句的情况下,该帐户被创建为解锁状态。
  • 与ALTER USER一起使用时,这些子句指定现有帐户的新锁定状态。 在没有任何子句的情况下,账户锁定状态保持不变。

帐户锁定状态记录在mysql.user表的account_locked列中。 SHOW CREATE USER的输出表明一个账户是否被锁定或解锁。

如果客户端尝试连接到锁定的帐户,则尝试失败。 服务器递增Locked_connects状态变量,该变量指示尝试连接到锁定帐户的次数,返回ER_ACCOUNT_HAS_BEEN_LOCKED错误并将消息写入错误日志中:

Access denied for user ‘user_name‘@’host_name‘. Account is locked.

锁定帐户不会影响使用假定已锁定帐户身份的代理用户进行连接。 它也不会影响执行存储程序或具有DEFINER子句命名锁定帐户的视图的能力。 也就是说,使用代理帐户或存储的程序或视图的能力不受锁定帐户的影响。

帐户锁定功能取决于mysql.user表中是否存在account_locked列。 要从旧版本升级到MySQL 5.7.6及更高版本,请运行mysql_upgrade以确保此列存在。 对于没有account_locked列的非升级安装,服务器将所有帐户视为解锁,并且使用ACCOUNT LOCK或ACCOUNT UNLOCK子句会产生错误。

6.3.12 SQL 基础的 MYSQL 账户活动审计

应用程序可以使用以下准则来执行将数据库活动与MySQL帐户关联的基于SQL的审计。

MySQL帐户对应于mysql.user表中的行。 当客户端连接成功时,服务器将客户端认证到此表中的特定行。 此行中的“User”和“Host”列值唯一标识帐户,并与’user_name”’host_name’格式相对应,其中帐户名称用SQL语句编写。

用于认证客户端的帐户决定了客户端拥有哪些权限。 通常,可以调用CURRENT_USER()函数来确定哪个帐户适用于客户端用户。 其值由帐户的user表行User和Host列构建。

但是,在某些情况下,CURRENT_USER()值不是与客户端用户相对应,而是与另一个帐户相对应。 这发生在特权检查不基于客户端帐户时的上下文中:

  • 存储的例程(过程和函数)使用SQL SECURITY DEFINER特性定义
  • 使用SQL SECURITY DEFINER特性定义的视图
  • 触发器和事件

在这些情况下,权限查是针对DEFINER帐户完成的,CURRENT_USER()指向该帐户,而不是针对调用存储例程或视图的客户端的帐户,或者是谁引起触发器激活的帐户。 要确定调用用户,可以调用USER()函数,该函数返回一个值,该值指示客户端和客户端连接的主机提供的实际用户名。 但是,此值不一定直接对应于用户表中的帐户,因为USER()值从不包含通配符,而帐户值(由CURRENT_USER()返回)可能包含用户名和主机名通配符

如果应用程序必须调用USER()进行用户审计(例如,如果它从触发器中进行审计),但也必须能够将USER()值与用户表中的帐户相关联,则必须避免 在用户或主机列中包含通配符。 具体而言,不允许用户为空(创建匿名用户帐户),并且不允许在主机值中使用模式字符或网络掩码表示法。 所有帐户必须具有非空User值和文字主机值。

关于前面的示例,应该更改“@’localhost’和’user2’@%.example.com’帐户不使用通配符:

RENAME USER ”@’localhost’ TO ‘user1’@’localhost’;

RENAME USER ‘user2’@’%.example.com’ TO ‘user2’@’remote.example.com’;

如果user2必须能够从http://example.com域中的多个主机连接,则每个主机应该有一个单独的帐户。

    原文作者:sybbjs
    原文地址: https://zhuanlan.zhihu.com/p/34688519
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞