MySQL 角色是权限的命名集合。与用户帐户一样,角色可以授予和撤销权限。
可以将角色授予用户帐户,从而将每个角色相关的权限授予帐户。这使得可以将权限集分配给帐户,并提供了一种方便的替代方法,可以为概念化所需的权限分配以及实现它们提供便利。
以下列表总结了 MySQL 提供的角色管理功能
CREATE ROLE
和DROP ROLE
创建和删除角色。SHOW GRANTS
显示用户帐户和角色的权限和角色分配。SET DEFAULT ROLE
指定默认情况下哪些帐户角色处于活动状态。SET ROLE
更改当前会话中的活动角色。CURRENT_ROLE()
函数显示当前会话中的活动角色。mandatory_roles
和activate_all_roles_on_login
系统变量允许定义强制角色以及用户登录服务器时自动激活授予的角色。
有关各个角色操作语句(包括使用它们的所需权限)的说明,请参见 第 15.7.1 节,“帐户管理语句”。以下讨论提供了角色用法的示例。除非另有说明,否则此处显示的 SQL 语句应使用具有足够管理权限的 MySQL 帐户执行,例如 root
帐户。
考虑以下情况
应用程序使用名为
app_db
的数据库。与应用程序相关联,可以有用于创建和维护应用程序的开发人员帐户,以及用于与应用程序交互的用户帐户。
开发人员需要对数据库的完全访问权限。一些用户只需要读取访问权限,而另一些用户则需要读写访问权限。
为了避免将权限单独授予可能有很多的用户帐户,请创建角色作为所需权限集的名称。这使得可以通过授予适当的角色来轻松地将所需权限授予用户帐户。
要创建角色,请使用 CREATE ROLE
语句
CREATE ROLE 'app_developer', 'app_read', 'app_write';
角色名称非常类似于用户帐户名称,并以 '
格式由用户部分和主机部分组成。主机部分如果省略,则默认为 user_name
'@'host_name
''%'
。用户部分和主机部分可以不加引号,除非它们包含特殊字符,例如 -
或 %
。与帐户名称不同,角色名称的用户部分不能为空。有关更多信息,请参见 第 8.2.5 节,“指定角色名称”。
要将权限分配给角色,请使用与将权限分配给用户帐户相同的语法执行 GRANT
语句
GRANT ALL ON app_db.* TO 'app_developer';
GRANT SELECT ON app_db.* TO 'app_read';
GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'app_write';
现在假设您最初需要一个开发者帐户,两个需要只读访问权限的用户帐户,以及一个需要读写访问权限的用户帐户。使用 CREATE USER
创建这些帐户。
CREATE USER 'dev1'@'localhost' IDENTIFIED BY 'dev1pass';
CREATE USER 'read_user1'@'localhost' IDENTIFIED BY 'read_user1pass';
CREATE USER 'read_user2'@'localhost' IDENTIFIED BY 'read_user2pass';
CREATE USER 'rw_user1'@'localhost' IDENTIFIED BY 'rw_user1pass';
要为每个用户帐户分配其所需的权限,您可以使用与刚刚显示的相同形式的 GRANT
语句,但这需要为每个用户列举单个权限。相反,请使用另一种 GRANT
语法,该语法允许授予角色而不是权限。
GRANT 'app_developer' TO 'dev1'@'localhost';
GRANT 'app_read' TO 'read_user1'@'localhost', 'read_user2'@'localhost';
GRANT 'app_read', 'app_write' TO 'rw_user1'@'localhost';
用于 rw_user1
帐户的 GRANT
语句授予了读和写角色,这两个角色结合起来提供了所需的读和写权限。
用于将角色授予帐户的 GRANT
语法不同于用于授予权限的语法:存在一个 ON
子句来分配权限,而没有 ON
子句来分配角色。由于语法不同,您不能在同一个语句中混合分配权限和角色。(允许将权限和角色都分配给帐户,但您必须使用单独的 GRANT
语句,每个语句都具有适合要授予内容的语法。)不能将角色授予匿名用户。
创建角色时,角色处于锁定状态,没有密码,并被分配默认的身份验证插件。(这些角色属性可以通过具有全局 CREATE USER
权限的用户使用 ALTER USER
语句稍后更改。)
在锁定状态下,角色无法用于向服务器进行身份验证。如果解锁,角色可以用于身份验证。这是因为角色和用户都是授权标识符,具有很多共同点,而且很少有区别。另请参见 用户和角色的可互换性。
可以通过在 mandatory_roles
系统变量的值中命名它们来指定强制角色。服务器将强制角色视为已授予所有用户,因此无需明确授予任何帐户。
要指定服务器启动时的强制角色,请在服务器 my.cnf
文件中定义 mandatory_roles
。
[mysqld]
mandatory_roles='role1,role2@localhost,r3@%.example.com'
要设置和持久化运行时的 mandatory_roles
,请使用以下语句
SET PERSIST mandatory_roles = 'role1,role2@localhost,r3@%.example.com';
SET PERSIST
为正在运行的 MySQL 实例设置一个值。它还会保存该值,使其在后续服务器重启时继续有效。要更改正在运行的 MySQL 实例的值,但不希望它在后续服务器重启时继续有效,请使用 GLOBAL
关键字而不是 PERSIST
。请参见 第 15.7.6.1 节,“SET 语法用于变量赋值”。
设置 mandatory_roles
需要 ROLE_ADMIN
权限,此外还需要 SYSTEM_VARIABLES_ADMIN
权限(或弃用的 SUPER
权限),这些权限通常用于设置全局系统变量。
强制角色与显式授予的角色一样,直到激活才会生效(请参见 激活角色)。在登录时,如果启用了 activate_all_roles_on_login
系统变量,则会为所有已授予角色进行角色激活,否则会为设置为默认角色的角色进行激活。在运行时,SET ROLE
会激活角色。
在 mandatory_roles
值中命名的角色不能使用 REVOKE
撤销,也不能使用 DROP ROLE
或 DROP USER
删除。
要防止会话默认情况下成为系统会话,具有 SYSTEM_USER
权限的角色不能列在 mandatory_roles
系统变量的值中。
如果在启动时将具有
SYSTEM_USER
权限的角色分配给mandatory_roles
,服务器会将一条消息写入错误日志并退出。如果在运行时将具有
SYSTEM_USER
权限的角色分配给mandatory_roles
,则会发生错误,并且mandatory_roles
值保持不变。
即使有这种保护措施,最好避免通过角色授予 SYSTEM_USER
权限,以防止可能发生的权限提升。
如果在 mandatory_roles
中命名的角色不存在于 mysql.user
系统表中,则该角色不会授予用户。当服务器尝试为用户激活角色时,它不会将不存在的角色视为强制角色,并会将警告写入错误日志。如果该角色后来创建并因此变为有效,可能需要 FLUSH PRIVILEGES
使服务器将其视为强制角色。
SHOW GRANTS
会根据 第 15.7.7.22 节,“SHOW GRANTS 语句” 中描述的规则显示强制角色。
要验证分配给帐户的权限,请使用 SHOW GRANTS
。例如
mysql> SHOW GRANTS FOR 'dev1'@'localhost';
+-------------------------------------------------+
| Grants for dev1@localhost |
+-------------------------------------------------+
| GRANT USAGE ON *.* TO `dev1`@`localhost` |
| GRANT `app_developer`@`%` TO `dev1`@`localhost` |
+-------------------------------------------------+
但是,它显示了每个授予的角色,而没有 “展开” 它来表示该角色所代表的权限。要显示角色权限,请添加一个 USING
子句,命名要显示权限的已授予角色。
mysql> SHOW GRANTS FOR 'dev1'@'localhost' USING 'app_developer';
+----------------------------------------------------------+
| Grants for dev1@localhost |
+----------------------------------------------------------+
| GRANT USAGE ON *.* TO `dev1`@`localhost` |
| GRANT ALL PRIVILEGES ON `app_db`.* TO `dev1`@`localhost` |
| GRANT `app_developer`@`%` TO `dev1`@`localhost` |
+----------------------------------------------------------+
以类似方式验证其他每个类型的用户。
mysql> SHOW GRANTS FOR 'read_user1'@'localhost' USING 'app_read';
+--------------------------------------------------------+
| Grants for read_user1@localhost |
+--------------------------------------------------------+
| GRANT USAGE ON *.* TO `read_user1`@`localhost` |
| GRANT SELECT ON `app_db`.* TO `read_user1`@`localhost` |
| GRANT `app_read`@`%` TO `read_user1`@`localhost` |
+--------------------------------------------------------+
mysql> SHOW GRANTS FOR 'rw_user1'@'localhost' USING 'app_read', 'app_write';
+------------------------------------------------------------------------------+
| Grants for rw_user1@localhost |
+------------------------------------------------------------------------------+
| GRANT USAGE ON *.* TO `rw_user1`@`localhost` |
| GRANT SELECT, INSERT, UPDATE, DELETE ON `app_db`.* TO `rw_user1`@`localhost` |
| GRANT `app_read`@`%`,`app_write`@`%` TO `rw_user1`@`localhost` |
+------------------------------------------------------------------------------+
SHOW GRANTS
会根据 第 15.7.7.22 节,“SHOW GRANTS 语句” 中描述的规则显示强制角色。
授予用户帐户的角色在帐户会话中可以处于活动状态或非活动状态。如果授予的角色在会话中处于活动状态,则其权限适用;否则,它们不适用。要确定当前会话中哪些角色处于活动状态,请使用 CURRENT_ROLE()
函数。
默认情况下,将角色授予帐户或在 mandatory_roles
系统变量值中命名角色,不会自动导致该角色在帐户会话中处于活动状态。例如,由于到目前为止,在前面的讨论中没有激活任何 rw_user1
角色,因此如果您以 rw_user1
的身份连接到服务器并调用 CURRENT_ROLE()
函数,则结果为 NONE
(没有活动角色)。
mysql> SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE |
+----------------+
要指定每次用户连接到服务器并进行身份验证时应激活哪些角色,请使用 SET DEFAULT ROLE
。要将默认值设置为先前创建的每个帐户的所有分配的角色,请使用以下语句
SET DEFAULT ROLE ALL TO
'dev1'@'localhost',
'read_user1'@'localhost',
'read_user2'@'localhost',
'rw_user1'@'localhost';
现在,如果您以 rw_user1
的身份连接,则 CURRENT_ROLE()
的初始值将反映新的默认角色分配。
mysql> SELECT CURRENT_ROLE();
+--------------------------------+
| CURRENT_ROLE() |
+--------------------------------+
| `app_read`@`%`,`app_write`@`%` |
+--------------------------------+
要使所有显式授予的角色和强制角色在用户连接到服务器时自动激活,请启用 activate_all_roles_on_login
系统变量。默认情况下,自动角色激活处于禁用状态。
在会话中,用户可以执行 SET ROLE
来更改活动角色集。例如,对于 rw_user1
mysql> SET ROLE NONE; SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE |
+----------------+
mysql> SET ROLE ALL EXCEPT 'app_write'; SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| `app_read`@`%` |
+----------------+
mysql> SET ROLE DEFAULT; SELECT CURRENT_ROLE();
+--------------------------------+
| CURRENT_ROLE() |
+--------------------------------+
| `app_read`@`%`,`app_write`@`%` |
+--------------------------------+
第一个 SET ROLE
语句会停用所有角色。第二个语句会使 rw_user1
实际上变为只读用户。第三个语句会恢复默认角色。
存储过程和视图对象的有效用户受 DEFINER
和 SQL SECURITY
属性影响,这些属性确定执行是在调用者上下文还是定义者上下文中进行(请参见 第 27.7 节,“存储对象访问控制”)。
在调用者上下文中执行的存储过程和视图对象会使用当前会话中处于活动状态的角色进行执行。
在定义者上下文中执行的存储过程和视图对象会使用其
DEFINER
属性中命名的用户的默认角色进行执行。如果启用了activate_all_roles_on_login
,则此类对象会使用授予DEFINER
用户的所有角色(包括强制角色)进行执行。对于存储过程,如果应该使用与默认角色不同的角色进行执行,则程序体可以执行SET ROLE
来激活所需的角色。这必须谨慎操作,因为分配给角色的权限可能会更改。
与角色可以授予帐户一样,也可以从帐户撤销角色。
REVOKE role FROM user;
在 mandatory_roles
系统变量值中命名的角色不能撤销。
REVOKE
也可以应用于角色以修改授予它的权限。这不仅会影响角色本身,还会影响授予该角色的任何帐户。假设您希望暂时使所有应用程序用户变为只读用户。为此,请使用 REVOKE
从 app_write
角色撤销修改权限。
REVOKE INSERT, UPDATE, DELETE ON app_db.* FROM 'app_write';
实际上,这会导致角色没有任何权限,如使用 SHOW GRANTS
(它演示了此语句不仅可以用于用户,也可以用于角色)可以看到的那样。
mysql> SHOW GRANTS FOR 'app_write';
+---------------------------------------+
| Grants for app_write@% |
+---------------------------------------+
| GRANT USAGE ON *.* TO `app_write`@`%` |
+---------------------------------------+
由于从角色撤销权限会影响分配了已修改角色的任何用户的权限,因此 rw_user1
现在没有任何表修改权限(INSERT
、UPDATE
和 DELETE
不再存在)。
mysql> SHOW GRANTS FOR 'rw_user1'@'localhost'
USING 'app_read', 'app_write';
+----------------------------------------------------------------+
| Grants for rw_user1@localhost |
+----------------------------------------------------------------+
| GRANT USAGE ON *.* TO `rw_user1`@`localhost` |
| GRANT SELECT ON `app_db`.* TO `rw_user1`@`localhost` |
| GRANT `app_read`@`%`,`app_write`@`%` TO `rw_user1`@`localhost` |
+----------------------------------------------------------------+
实际上,rw_user1
读写用户已经变成了只读用户。这也适用于授予 app_write
角色的任何其他帐户,说明了使用角色如何使修改各个帐户的权限变得不必要。
要恢复角色的修改权限,只需重新授予它们即可。
GRANT INSERT, UPDATE, DELETE ON app_db.* TO 'app_write';
现在 rw_user1
再次拥有修改权限,授予 app_write
角色的任何其他帐户也是如此。
要删除角色,请使用 DROP ROLE
DROP ROLE 'app_read', 'app_write';
删除角色会将其从授予该角色的每个帐户中撤销。
在 mandatory_roles
系统变量值中命名的角色不能删除。
如前所述,对于 SHOW GRANTS
,它显示用户帐户或角色的授权,帐户和角色可以互换使用。
角色和用户之间的区别在于 CREATE ROLE
创建一个默认情况下被锁定的授权标识符,而 CREATE USER
创建一个默认情况下未锁定的授权标识符。请记住,这种区别不是一成不变的;具有适当权限的用户可以在创建用户或角色后锁定或解锁角色或(其他)用户。
如果数据库管理员希望某个特定的授权标识符必须是角色,则可以使用命名方案来传达此意图。例如,您可以对所有打算作为角色且仅作为角色的授权标识符使用 r_
前缀。
角色和用户之间的另一个区别在于管理它们可用的权限。
CREATE ROLE
和DROP ROLE
权限分别仅允许使用CREATE ROLE
和DROP ROLE
语句。CREATE USER
权限允许使用ALTER USER
、CREATE ROLE
、CREATE USER
、DROP ROLE
、DROP USER
、RENAME USER
和REVOKE ALL PRIVILEGES
语句。
因此,CREATE ROLE
和 DROP ROLE
权限不如 CREATE USER
强大,可以授予仅被允许创建和删除角色,而不执行更一般的帐户操作的用户。
关于用户和角色的权限和可互换性,您可以像角色一样对待用户帐户,并将该帐户授予另一个用户或角色。效果是将帐户的权限和角色授予另一个用户或角色。
这组语句演示了您可以将用户授予用户、将角色授予用户、将用户授予角色或将角色授予角色。
CREATE USER 'u1';
CREATE ROLE 'r1';
GRANT SELECT ON db1.* TO 'u1';
GRANT SELECT ON db2.* TO 'r1';
CREATE USER 'u2';
CREATE ROLE 'r2';
GRANT 'u1', 'r1' TO 'u2';
GRANT 'u1', 'r1' TO 'r2';
在每种情况下,结果都是将与授予对象关联的权限授予被授予对象。执行这些语句后,u2
和 r2
都获得了来自用户 (u1
) 和角色 (r1
) 的权限。
mysql> SHOW GRANTS FOR 'u2' USING 'u1', 'r1';
+-------------------------------------+
| Grants for u2@% |
+-------------------------------------+
| GRANT USAGE ON *.* TO `u2`@`%` |
| GRANT SELECT ON `db1`.* TO `u2`@`%` |
| GRANT SELECT ON `db2`.* TO `u2`@`%` |
| GRANT `u1`@`%`,`r1`@`%` TO `u2`@`%` |
+-------------------------------------+
mysql> SHOW GRANTS FOR 'r2' USING 'u1', 'r1';
+-------------------------------------+
| Grants for r2@% |
+-------------------------------------+
| GRANT USAGE ON *.* TO `r2`@`%` |
| GRANT SELECT ON `db1`.* TO `r2`@`%` |
| GRANT SELECT ON `db2`.* TO `r2`@`%` |
| GRANT `u1`@`%`,`r1`@`%` TO `r2`@`%` |
+-------------------------------------+
前面的示例仅是说明性的,但用户帐户和角色的可互换性具有实际应用,例如在以下情况下:假设一个传统应用程序开发项目在 MySQL 中出现角色之前就开始了,因此与该项目关联的所有用户帐户都直接被授予权限(而不是通过被授予角色而被授予权限)。这些帐户之一是开发人员帐户,最初被授予以下权限
CREATE USER 'old_app_dev'@'localhost' IDENTIFIED BY 'old_app_devpass';
GRANT ALL ON old_app.* TO 'old_app_dev'@'localhost';
如果这位开发人员离开了项目,则需要将权限分配给另一个用户,或者如果开发活动扩展了,则可能需要分配给多个用户。以下是处理该问题的几种方法。
不使用角色:更改帐户密码,以便原始开发人员无法使用它,并让新开发人员使用该帐户。
ALTER USER 'old_app_dev'@'localhost' IDENTIFIED BY 'new_password';
使用角色:锁定帐户以防止任何人使用它连接到服务器。
ALTER USER 'old_app_dev'@'localhost' ACCOUNT LOCK;
然后将该帐户视为角色。对于每个新加入项目的新开发人员,创建一个新帐户,并将原始开发人员帐户授予该帐户。
CREATE USER 'new_app_dev1'@'localhost' IDENTIFIED BY 'new_password'; GRANT 'old_app_dev'@'localhost' TO 'new_app_dev1'@'localhost';
效果是将原始开发人员帐户权限分配给新帐户。