### 6.2.10 使用角色

[](<>)

MySQL 角色是特权的命名集合。与用户帐户一样，角色可以授予和撤销它们的权限。

可以向用户帐户授予角色，这将授予该帐户与每个角色相关联的权限。这允许将权限集分配给帐户，并为授予个人权限提供了一种方便的替代方法，用于概念化所需的权限分配和实施它们。

以下列表总结了 MySQL 提供的角色管理功能：

-   [`创建角色`](create-role.html)和[`删除角色`](drop-role.html)创建和删除角色。

-   [`授予`](grant.html)和[`撤销`](revoke.html)分配权限以撤销用户帐户和角色的权限。

-   [`显示赠款`](show-grants.html)显示用户帐户和角色的权限和角色分配。

-   [`设置默认角色`](set-default-role.html)指定默认情况下哪些帐户角色处于活动状态。

-   [`设定角色`](set-role.html)更改当前会话中的活动角色。

-   这[`目前角色（）`](information-functions.html#function_current-role)函数显示当前会话中的活动角色。

-   这[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)和[`activate_all_roles_on_login`](server-system-variables.html#sysvar_activate_all_roles_on_login)系统变量允许在用户登录服务器时定义强制角色和自动激活授予的角色。

    有关各个角色操作语句（包括使用它们所需的权限）的描述，请参阅[第 13.7.1 节，“账户管理声明”](account-management-statements.html).以下讨论提供了角色使用的示例。除非另有说明，否则此处显示的 SQL 语句应使用具有足够管理权限的 MySQL 帐户执行，例如`根`帐户。

-   [创建角色并授予他们权限](roles.html#roles-creating-granting)

-   [定义强制角色](roles.html#mandatory-roles)

-   [检查角色权限](roles.html#roles-checking)

-   [激活角色](roles.html#roles-activating)

-   [撤销角色或角色特权](roles.html#roles-revoking)

-   [删除角色](roles.html#roles-dropping)

-   [用户和角色可互换性](roles.html#role-user-interchangeability)

#### 创建角色并授予他们权限

考虑这种情况：

-   一个应用程序使用一个名为`app_db`.

-   与应用程序相关联的帐户可以用于创建和维护应用程序的开发人员以及与之交互的用户。

-   开发人员需要完全访问数据库。一些用户只需要读取权限，其他用户需要读/写权限。

    为避免将权限单独授予可能的多个用户帐户，请创建角色作为所需权限集的名称。通过授予适当的角色，这使得向用户帐户授予所需的权限变得容易。

    要创建角色，请使用[`创建角色`](create-role.html)陈述：


```
CREATE ROLE 'app_developer', 'app_read', 'app_write';
```

角色名称很像用户帐户名称，由用户部分和主机部分组成`'*`用户名`*'@'*`主机名`*'`格式。主机部分，如果省略，默认为`'%'`.用户和主机部分可以不加引号，除非它们包含特殊字符，例如`-`要么`%`.与账户名不同，角色名的用户部分不能为空。有关其他信息，请参阅[第 6.2.5 节，“指定角色名称”](role-names.html).

要为角色​​分配权限，请执行[`授予`](grant.html)使用与为用户帐户分配权限相同的语法的语句：

```
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.html)创建帐户：

```
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.html)与刚刚显示的形式相同的语句，但这需要为每个用户枚举单独的权限。相反，使用替代方案[`授予`](grant.html)允许授予角色而不是特权的语法：

```
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';
```

这[`授予`](grant.html)的声明`rw_user1`account 授予读取和写入角色，它们结合起来提供所需的读取和写入权限。

这[`授予`](grant.html)向帐户授予角色的语法与授予权限的语法不同：有一个`在`条款来分配特权，而没有`在`子句来分配角色。由于语法不同，您不能在同一语句中混合分配权限和角色。（允许将权限和角色分配给一个帐户，但您必须使用单独的[`授予`](grant.html)语句，每个语句的语法都适合被授予的内容。）从 MySQL 8.0.16 开始，不能将角色授予匿名用户。

创建时的角色被锁定，没有密码，并被分配了默认的身份验证插件。（这些角色属性可以稍后使用[`更改用户`](alter-user.html)声明，由拥有全球[`创建用户`](privileges-provided.html#priv_create-user)特权。）

锁定时，角色不能用于对服务器进行身份验证。如果解锁，则可以使用角色进行身份验证。这是因为角色和用户都是授权标识符，它们有很多共同点，但几乎没有区别。也可以看看[用户和角色可互换性](roles.html#role-user-interchangeability).

#### 定义强制角色

可以通过在[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)系统变量。服务器将强制角色视为授予所有用户，因此无需明确授予任何帐户。

要在服务器启动时指定强制角色，请定义[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)在您的服务器中`我的.cnf`文件：

```
[mysqld]
mandatory_roles='role1,role2@localhost,r3@%.example.com'
```

设置和坚持[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)在运行时，使用如下语句：

```
SET PERSIST mandatory_roles = 'role1,role2@localhost,r3@%.example.com';
```

[`设置坚持`](set-variable.html)为正在运行的 MySQL 实例设置一个值。它还保存该值，使其延续到后续服务器重新启动。要更改正在运行的 MySQL 实例的值而不使其延续到后续重新启动，请使用`全球的`关键字而不是`坚持`.看[第 13.7.6.1 节，“变量赋值的 SET 语法”](set-variable.html).

环境[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)需要[`ROLE_ADMIN`](privileges-provided.html#priv_role-admin)特权，除了[`SYSTEM_VARIABLES_ADMIN`](privileges-provided.html#priv_system-variables-admin)特权（或已弃用的[`极好的`](privileges-provided.html#priv_super)特权）通常需要设置全局系统变量。

强制角色，如明确授予的角色，在激活之前不会生效（请参阅[激活角色](roles.html#roles-activating)）。在登录时，如果[`activate_all_roles_on_login`](server-system-variables.html#sysvar_activate_all_roles_on_login)系统变量已启用，否则对于设置为默认角色的角色。在运行时，[`设定角色`](set-role.html)激活角色。

以值命名的角色[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)不能撤销[`撤销`](revoke.html)或与[`删除角色`](drop-role.html)要么[`删除用户`](drop-user.html).

为了防止会话默认成为系统会话，具有[`SYSTEM_USER`](privileges-provided.html#priv_system-user)特权不能在值中列出[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)系统变量：

-   如果[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)在启动时分配了一个角色，该角色具有[`SYSTEM_USER`](privileges-provided.html#priv_system-user)特权，服务器将消息写入错误日志并退出。

-   如果[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)在运行时分配了一个角色，该角色具有[`SYSTEM_USER`](privileges-provided.html#priv_system-user)特权，发生错误并且[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)值保持不变。

    即使有这种保障措施，最好还是避免授予[`SYSTEM_USER`](privileges-provided.html#priv_system-user)通过角色授予权限，以防止权限升级的可能性。

    如果一个角色在[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)不存在于`mysql.user`系统表，角色不授予用户。当服务器尝试为用户激活角色时，它不会将不存在的角色视为强制性角色并将警告写入错误日志。如果角色稍后创建并因此变得有效，[`刷新特权`](flush.html#flush-privileges)可能有必要使服务器将其视为强制性的。

[`显示赠款`](show-grants.html)根据描述的规则显示强制角色[第 13.7.7.21 节，“显示授予声明”](show-grants.html).

#### 检查角色权限

要验证分配给帐户的权限，请使用[`显示赠款`](show-grants.html).例如：

```
mysql> SHOW GRANTS FOR 'dev1'@'localhost';
+-------------------------------------------------+
| Grants for dev1@localhost                       |
+-------------------------------------------------+
| GRANT USAGE ON *.* TO `dev1`@`localhost`        |
| GRANT `app_developer`@`%` TO `dev1`@`localhost` |
+-------------------------------------------------+
```

但是，这显示了每个授予的角色，而没有将其“扩展”到该角色所代表的特权。要同时显示角色权限，请添加`使用`命名要为其显示权限的授予角色的子句：

```
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.html)根据描述的规则显示强制角色[第 13.7.7.21 节，“显示授予声明”](show-grants.html).

#### 激活角色

授予用户帐户的角色在帐户会话中可以是活动的或非活动的。如果授予的角色在会话中处于活动状态，则其权限适用；否则，他们不会。要确定当前会话中哪些角色处于活动状态，请使用[`目前角色（）`](information-functions.html#function_current-role)功能。

默认情况下，将角色授予帐户或在[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)系统变量值不会自动导致角色在帐户会话中变为活动状态。例如，因为到目前为止在前面的讨论中没有`rw_user1`如果您以身份连接到服务器，角色已被激活`rw_user1`并调用[`目前角色（）`](information-functions.html#function_current-role)函数，结果是`没有`（无活跃角色）：

```
mysql> SELECT CURRENT_ROLE();
+----------------+
| CURRENT_ROLE() |
+----------------+
| NONE           |
+----------------+
```

要指定每次用户连接到服务器并进行身份验证时应该激活哪些角色，请使用[`设置默认角色`](set-default-role.html).要将默认设置为之前创建的每个帐户的所有分配角色，请使用以下语句：

```
SET DEFAULT ROLE ALL TO
  'dev1'@'localhost',
  'read_user1'@'localhost',
  'read_user2'@'localhost',
  'rw_user1'@'localhost';
```

现在，如果您连接为`rw_user1`, 的初始值[`目前角色（）`](information-functions.html#function_current-role)反映了新的默认角色分配：

```
mysql> SELECT CURRENT_ROLE();
+--------------------------------+
| CURRENT_ROLE()                 |
+--------------------------------+
| `app_read`@`%`,`app_write`@`%` |
+--------------------------------+
```

要在用户连接到服务器时自动激活所有明确授予和强制的角色，请启用[`activate_all_roles_on_login`](server-system-variables.html#sysvar_activate_all_roles_on_login)系统变量。默认情况下，自动角色激活被禁用。

在会话中，用户可以执行[`设定角色`](set-role.html)更改活动角色集。例如，对于`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.html)语句停用所有角色。第二个使`rw_user1`有效只读。第三个恢复默认角色。

[](<>)[](<>)[](<>)[](<>)

存储程序和视图对象的有效用户受`定义者`和`SQL 安全`属性，确定执行是在调用者还是定义者上下文中发生（参见[第 25.6 节，“存储对象访问控制”](stored-objects-security.html)):

-   在调用者上下文中执行的存储程序和视图对象与在当前会话中活动的角色一起执行。

-   在定义器上下文中执行的存储程序和视图对象使用在其命名的用户的默认角色执行`定义者`属性。如果[`activate_all_roles_on_login`](server-system-variables.html#sysvar_activate_all_roles_on_login)启用后，此类对象将使用授予的所有角色执行`定义者`用户，包括强制角色。对于存储的程序，如果应该以不同于默认角色的角色执行，则程序主体可以执行[`设定角色`](set-role.html)激活所需的角色。由于可以更改分配给角色的权限，因此必须谨慎执行此操作。

#### 撤销角色或角色特权

就像角色可以被授予一个帐户一样，它们可以从一个帐户中撤消：

```
REVOKE role FROM user;
```

中命名的角色[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)系统变量值不能被撤销。

[`撤销`](revoke.html)也可以应用于角色以修改授予它的权限。这不仅会影响角色本身，还会影响授予该角色的任何帐户。假设您想暂时将所有应用程序用户设为只读。为此，请使用[`撤销`](revoke.html)撤销修改权限`app_write`角色：

```
REVOKE INSERT, UPDATE, DELETE ON app_db.* FROM 'app_write';
```

碰巧的是，这使该角色根本没有特权，如使用所示[`显示赠款`](show-grants.html)（这表明该语句可以与角色一起使用，而不仅仅是用户）：

```
mysql> SHOW GRANTS FOR 'app_write';
+---------------------------------------+
| Grants for app_write@%                |
+---------------------------------------+
| GRANT USAGE ON *.* TO `app_write`@`%` |
+---------------------------------------+
```

因为撤销角色的权限会影响分配给修改角色的任何用户的权限，`rw_user1`现在没有表修改权限（[`插入`](privileges-provided.html#priv_insert),[`更新`](privileges-provided.html#priv_update)， 和[`删除`](privileges-provided.html#priv_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.html)：

```
DROP ROLE 'app_read', 'app_write';
```

删除角色会从授予该角色的每个帐户中撤消该角色。

中命名的角色[`强制角色`](server-system-variables.html#sysvar_mandatory_roles)系统变量值不能被删除。

#### 用户和角色可互换性

如前所述[`显示赠款`](show-grants.html)，它显示对用户帐户或角色的授权，帐户和角色可以互换使用。

角色和用户之间的一个区别是[`创建角色`](create-role.html)创建一个默认锁定的授权标识符，而[`创建用户`](create-user.html)创建一个默认解锁的授权标识符。但是，区别不是一成不变的，因为具有适当权限的用户可以在创建角色或用户后锁定或解锁角色或用户。

如果数据库管理员偏好特定的授权标识符必须是角色，则可以使用名称方案来传达此意图。例如，您可以使用`r_`您打算成为角色的所有授权标识符的前缀，仅此而已。

角色和用户之间的另一个区别在于可用于管理它们的权限：

-   这[`创建角色`](privileges-provided.html#priv_create-role)和[`删除角色`](privileges-provided.html#priv_drop-role)权限只允许使用[`创建角色`](create-role.html)和[`删除角色`](drop-role.html)声明，分别。

-   这[`创建用户`](privileges-provided.html#priv_create-user)特权允许使用[`更改用户`](alter-user.html), [`创建角色`](create-role.html), [`创建用户`](create-user.html), [`删除角色`](drop-role.html), [`删除用户`](drop-user.html), [`重命名用户`](rename-user.html)， 和[`撤销所有特权`](revoke.html)陈述。

    因此[`创建角色`](privileges-provided.html#priv_create-role)和[`放弃角色`](privileges-provided.html#priv_drop-role)特权没有权力那么强大[`创建用户`](privileges-provided.html#priv_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';
    ```

    其效果是将原始开发人员帐户权限分配给新帐户。
