简介
访问控制和用户管理是两个随着系统中用户数量和不同数据库实体数量的增加,会迅速变得复杂化的领域。管理各种数据库对象上的许多不同权限,确保具有相同职责的用户拥有相同的访问级别,以及审计和缩小访问范围,都会随着时间的推移而变得更加困难。
为了解决这个问题,MySQL 引入了“角色”的概念,允许您将一系列权限打包在一个给定的名称下,从而可以批量分配和修改设置。在本指南中,我们将介绍角色在 MySQL 中如何工作,以及如何使用它们来简化用户数据访问的管理。
命令
以下是我们将要讨论的与管理 MySQL 角色相关的主要 SQL 命令。
CREATE ROLE:CREATE ROLE命令在数据库系统中定义一个新角色。DROP ROLE:DROP ROLE命令执行相反的操作,删除一个现有角色。GRANT:GRANT命令有两个与角色相关的不同目的:向角色添加权限,以及将用户账户添加为角色成员。REVOKE: 在角色的上下文中,REVOKE命令从角色中移除权限,并从用户账户中移除角色成员资格。SHOW GRANTS:SHOW GRANTS命令显示给定用户账户或角色的权限。SET ROLE:SET ROLE命令更改用户账户当前正在使用的角色。这允许您决定在会话中哪些权限集适用于该账户。SET DEFAULT ROLE:SET DEFAULT ROLE命令定义当客户端以特定用户账户登录时自动应用的那些角色。
所需权限
要跟随本指南,您需要以下权限:
CREATE ROLEGRANT OPTIONCREATE USER(为其他用户设置默认角色)ROLE_ADMIN(设置修改角色行为的系统变量)SYSTEM_VARIABLES_ADMIN(设置修改角色行为的系统变量)
CREATE ROLE 权限是 CREATE USER 权限的次级版本,允许您创建和管理角色。已经拥有 CREATE USER 权限的账户自动拥有管理角色所需的所有功能。
GRANT OPTION 权限是向角色分配权限所必需的。您必须为您希望分配给角色的任何权限启用 GRANT OPTION。
什么是角色?
在 MySQL 中,角色是一个实体,它充当权限的容器或集合。管理员可以像向用户账户分配权限一样,向角色分配权限。然后,您可以将用户账户添加为角色的成员,从而允许这些账户访问与该角色关联的权限。
基本上,角色是一种将不同相关权限捆绑在一起,使权限管理变得更容易的方式。与其通过分配单个权限来确保每个用户都拥有他们所需的精确访问级别,不如使用命名的权限组来管理更少、更容易理解的分配。
这在分配访问级别时具有明显的优势,因为为用户分配 developer、sysadmin 或 financeteam 角色比单独管理几十个权限更容易。它还使得一次性快速调整多个账户的访问权限变得可能。如果您为销售团队创建一个新数据库,您可以将 salesteam 角色授予它,而不必追踪每个应该拥有访问权限的账户。
创建角色
如果您有一个拥有 CREATE ROLE 权限的账户,您可以使用 CREATE ROLE 命令管理角色。
MySQL 角色的语法是什么?
角色名称必须遵循特定格式,MySQL 才会认为它们是有效的。在许多方面,它们与定义 MySQL 用户账户的格式相似,但有一些重要的区别。
角色遵循以下格式:
'<role>'@'<host>'
与用户一样,角色有两个组成部分:角色名称和客户端连接的主机。但是,MySQL 解释这些组成部分的方式有所不同。
对于角色,名称的 '<role>' 部分不能为空。没有像用户那样“匿名”角色的概念。另一方面,省略 '<host>' 部分仍然允许,MySQL 将使用 % 作为主机。然而,在这种情况下,% 被解释为字面字符,而不是通配符。
实际上,这意味着尽管角色名称表面上与用户账户名称的格式相同,但它们不像用户账户那样进行任何类型的评估,而只是一个带有两个组成部分的标签。它们确实有两个部分的原因是,您可以创建既可以作为用户又可以作为角色的用户账户。当用作用户时,这些组成部分受用户管理文章中描述的特殊评估规则的约束,当用作角色时,名称仅使用字面组成部分名称直接匹配。
由于这些规则,在许多情况下,管理员选择仅使用 '<role>' 部分来定义角色。这会导致 MySQL 将字面字符 % 替换 '<host>' 部分,从而有效地使该部分名称不可见且无关紧要。如果您不打算将名称同时用作用户账户和角色,您也可以这样做。
如何创建角色?
要创建新角色,请使用 CREATE ROLE 命令。
基本语法如下:
CREATE ROLE '<role>'@'<host>';
您也可以通过用逗号分隔每个角色名称来同时创建多个角色:
CREATE ROLE '<role_1>'@'<host>', '<role_2>'@'<host>', '<role_3>'@'<host>';
如果指定的任何角色已存在于系统中,则该命令将失败并显示错误。
为避免这种情况并使 MySQL 只发出警告,您可以在角色名称之前,在 CREATE ROLE 命令之后包含 IF NOT EXISTS 子句:
CREATE ROLE IF NOT EXISTS '<role>'@'<host>';
如上所述,很多时候管理员为了简单起见会省略角色名称的 '<host>' 部分,隐式地将其设置为字面字符 %。因此,在实践中,您的许多角色创建命令可能更像这样:
CREATE ROLE '<role>';
如何授予角色权限?
创建新角色后,您的下一个优先事项通常是通过授予它们权限来使其有意义。
您授予角色权限的方式与您授予用户账户权限的方式相同。您提供希望授予的精确权限,通过提供数据库和权限有效的数据库对象来指定范围,以及应该授予权限的实体——在本例中是一个角色:
GRANT <privileges> ON <database>.<object> TO '<role>'@'<host>';
例如,要授予名为 readapp 的角色对 appdb 数据库及其包含的所有对象的 SELECT 权限,您可以输入:
GRANT SELECT ON appdb.* TO 'readapp';
同样,您可以通过输入以下命令,将同一数据库的写入权限授予名为 writeapp 的角色:
GRANT SELECT,INSERT,UPDATE,DELETE ON appdb.* TO 'writeapp';
您可以像直接对用户账户操作一样,向角色授予权限并从角色撤销权限。因此,如果您需要调整希望提供的访问级别,您可以随时修改与角色关联的权限。
如何将用户添加为角色成员?
一旦您将权限添加到您的角色中,您就可以开始将成员添加到该角色中,以授予他们关联的权限。
为此,MySQL 使用与我们用于向用户和角色授予权限的 GRANT 命令不同的形式。然而,这种新形式将角色添加到用户,允许用户账户访问赋予该角色的所有权限。
基本语法如下:
GRANT '<role>'@'<host>' TO '<user>'@'<host>';
例如,如果 'reports'@'localhost' 用户需要能够从 appdb 数据库读取数据以生成报告,我们可以将 readapp 角色添加到该用户账户,从而赋予它选择权限:
GRANT 'readapp' TO 'reports'@'localhost';
同样,要赋予 'appuser'@'localhost' 管理同一数据库中数据的能力,我们可以将该用户设为 writeapp 角色的成员:
GRANT 'writeapp' TO 'appuser'@'localhost';
'appuser'@'localhost' 账户现在将能够从数据库中插入、更新和删除数据。如果新的权限被添加到 writeapp 角色中,'appuser'@'localhost' 账户将立即获得这些权限。
如何自动授予每个用户特定角色?
有时可能存在您希望系统上的每个用户都能访问的角色。您可以通过设置 mandatory_roles 变量来定义每个账户自动被授予哪些角色。
要修改 mandatory_roles 变量,您的用户必须拥有 ROLE_ADMIN 和 SYSTEM_VARIABLES_ADMIN 权限。您可以通过输入以下命令来设置希望自动授予每个用户的角色:
SET PERSIST mandatory_roles = '`<role_1>`@`<host>`, `<role_2>`@`<host>`, `<role_3>`@`<host>`';
在这里,我们自动为系统上的每个用户授予三个角色。设置系统变量时,mandatory_roles 的值必须是一个字符串,因此我们将整个角色列表用单引号括起来,并使用反引号引用单个角色组件。
您不能将拥有 SYSTEM_USER 权限的角色添加到 mandatory_roles 列表中。这是一项安全措施,以确保系统上的所有会话不会自动成为系统会话。
如何使用角色的权限?
一旦您授予用户账户角色成员资格,您如何使用它们?要访问账户通过角色获得的权限,该角色必须被激活。
查看当前活动的角色
在激活新角色之前,您可以检查您的用户会话当前哪些角色是活动的。
要查看会话的活动角色,请键入:
SELECT CURRENT_ROLE()
输出将显示您当前会话中活动的零个或多个角色。与这些角色关联的权限将增加您被允许执行的操作。
如何为会话激活角色
要更改会话期间哪些角色处于活动状态,请使用 SET ROLE 命令。您可以通过多种不同方式使用此命令。
基本语法如下:
SET ROLE '<rolename>'@'<host>';
这将激活相关角色。需要注意的是,任何之前活动但未在 SET ROLE 命令中提及的角色都将停用。
要同时激活多个角色,请用逗号分隔每个角色:
SET ROLE '<role_1>'@'<host>', '<role_2>'@'<host>', '<role_3>'@'<host>';
要激活已授予您账户的所有角色,您可以指定 ALL 而非特定角色:
SET ROLE ALL;
您还可以告诉 MySQL 激活所有角色,除了使用 ALL EXCEPT 指定的例外:
SET ROLL ALL EXCEPT '<role_1>'@'<host>';
另一个选项是通过指定 NONE 来禁用您账户上的所有角色:
SET ROLE NONE
这将停用您用户在会话中的所有角色,只为您提供专门分配给您用户账户的权限。
要回到为您的账户定义的默认角色列表,请使用 DEFAULT 关键字:
SET ROLE DEFAULT
如何为用户账户定义默认角色
当您以用户身份登录时自动激活的角色以及当您使用 SET ROLE DEFAULT 时重新激活的角色都是可配置的。
要定义默认激活的角色,请使用 SET DEFAULT ROLE 命令,其用法类似于 SET ROLE 命令:
SET DEFAULT ROLE '<role_1>'@'<host>';
这将设置您自己的账户在登录时或使用 SET ROLE DEFAULT 时将激活的默认角色。
如果您的用户拥有 CREATE USER 权限,您可以为其他账户设置默认角色:
SET DEFAULT ROLE ALL TO '<user>'@'<host>';
在这里,我们指定 '<user>'@'<host>' 账户在认证时应自动激活其所有角色。
此语法也可用于为多个账户定义默认角色,只需用逗号分隔每个用户即可:
SET DEFAULT ROLE ALL TO '<user_1>'@'<host>', '<user_2>'@'<host>';
默认激活所有用户的全部角色
如果您希望 MySQL 服务器上的每个账户都默认激活其所有角色,您可以更改系统设置来实现这一点。
当 activate_all_roles_on_login 变量设置为 true 时,MySQL 将在登录时自动激活与账户关联的所有角色。这会覆盖 SET DEFAULT ROLE 指定的设置。
要启用此功能,您必须拥有 SYSTEM_VARIABLES_ADMIN 和 ROLE_ADMIN 权限。通过键入以下命令启用此功能:
SET PERSIST activate_all_roles_on_login = ON;
这将导致用户账户在登录时自动激活所有角色。但是,SET ROLE DEFAULT 仍允许您仅激活与账户关联的默认角色。
显示从角色获得的现有权限
要了解您的账户有哪些可用权限,您可以使用 SHOW GRANTS 命令。
要检查为用户启用的授权,请键入:
SHOW GRANTS FOR '<user>'@'<host>';
输出将显示直接分配给用户账户的所有权限以及该用户所属的所有角色。
了解账户所属的角色后,您可以通过输入以下命令来检查该角色为用户提供的权限:
SHOW GRANTS FOR '<user>'@'<host>' USING '<role>'@'<host>';
例如,要检查 'reports'@'localhost' 用户的权限,包括通过其 readapp 角色成员资格获得的权限,您可以使用:
SHOW GRANTS FOR 'reports'@'localhost' USING 'readapp';
这将显示明确授予 'reports'@'localhost' 用户账户的所有权限,以及 readapp 角色添加的权限。
从用户撤销角色
那么,当您想从用户那里移除角色时会发生什么?类似于 GRANT 命令既可以向用户或角色添加新权限,也可以向用户添加角色,REVOKE 命令可以从用户或角色中移除权限,也可以从用户中移除角色成员资格。
用于从用户账户移除角色的基本语法如下所示:
REVOKE '<role>' FROM '<user>'@'<host>';
执行此语句后,用户将不再拥有通过角色授予的权限。
举例来说,我们可以通过键入以下命令,从 'appuser'@'localhost' 用户账户撤销 writeapp 角色:
REVOKE 'writeapp' FROM 'appuser'@'localhost';
然而,如果用户通过其他方式(无论是直接授予还是通过与其他角色的成员资格授予)获得了权限,他们仍然拥有该权限。因此,如果 'appuser'@'localhost' 用户也是我们之前授予的 readapp 角色的成员,他们仍然拥有对 appdb 数据库的 SELECT 权限。
结论
在 MySQL 数据库中使用角色来分发权限可以帮助简化访问控制系统的管理开销和复杂性。使用角色比直接授予许多不同的权限更容易确保具有相同职责的用户拥有相同的权限。
同样,角色允许您明确权限授予背后的意图。与其向账户授予大量权限而不加任何说明,不如精心选择的角色名称可以帮助区分不同的访问原因。通过花时间提前创建和组织角色,从长远来看,您管理用户访问数据不同部分的能力将更加简单。
常见问题解答
为了执行刷新权限操作,即告诉服务器重新加载授权表,您可以发出 FLUSH PRIVILEGES 语句。
这也可以通过执行 mysqladmin flush-privileges 或 mysqladmin reload 命令来完成。
您可以使用 ALL 或 ALL PRIVILEGES 简写形式,在特定范围内向用户分配完全权限。
以下语法将授予 'salesadmin'@'localhost' 用户与 sales 数据库相关的所有权限,这些权限是您的用户能够授予的。
GRANT ALL PRIVILEGES ON sales.* TO 'salesadmin'@'localhost';
要全局授予用户只读权限,您可以使用通配符作为范围组件的数据库和数据库对象部分。
基本语法如下:
GRANT SELECT ON *.* TO 'sally'@localhost';
要将只读授予的范围限制为单个数据库,请将点左侧的通配符替换为数据库名称:
GRANT SELECT ON account.* TO 'meredith'@'localhost';
同样,要仅允许访问数据库中的特定表,请使用以下命令:
GRANT SELECT ON account.revenue TO 'meredith'@'localhost';
要在 MySQL 中创建一个新的 root 或超级用户账户,您必须使用 GRANT ALL PRIVILEGES 语句授予它对数据库中所有内容的完全 root 访问权限。
基本语法如下:
GRANT ALL PRIVILEGES ON *.* TO 'user_name'@'localhost' WITH GRANT OPTION;
要了解您的账户有哪些可用权限,您可以使用 SHOW GRANTS 命令。
要检查为用户启用的授权,请键入:
SHOW GRANTS FOR '<user>'@'<host>';
了解账户所属的角色后,您可以通过输入以下命令来检查该角色为用户提供的权限:
SHOW GRANTS FOR '<user>'@'<host>' USING '<role>'@'<host>';
