### 6.2.12 使用部分撤销的权限限制

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

在 MySQL 8.0.16 之前，除了某些模式之外，不能授予全局适用的权限。从 MySQL 8.0.16 开始，如果[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)系统变量已启用。具体来说，对于在全局级别具有权限的用户，[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)允许撤销特定模式的权限，同时保留其他模式的权限。因此施加的权限限制可能对管理具有全局权限但不应被允许访问某些模式的帐户很有用。例如，可以允许一个账户修改任何表，除了那些在`mysql`系统架构。

-   [使用部分撤销](partial-revokes.html#partial-revokes-usage)

-   [部分撤销与显式模式授权](partial-revokes.html#partial-revokes-versus-schema-grants)

-   [禁用部分撤销](partial-revokes.html#partial-revokes-disabling)

-   [部分撤销和复制](partial-revokes.html#partial-revokes-replication)

笔记

为简洁起见，[`创建用户`](create-user.html)此处显示的语句不包括密码。对于生产用途，请始终分配帐户密码。

#### 使用部分撤销

这[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)系统变量控制是否可以对帐户设置权限限制。默认，[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)被禁用并尝试部分撤销全局权限会产生错误：

```
mysql> CREATE USER u1;
mysql> GRANT SELECT, INSERT ON *.* TO u1;
mysql> REVOKE INSERT ON world.* FROM u1;
ERROR 1141 (42000): There is no such grant defined for user 'u1' on host '%'
```

为了允许[`撤销`](revoke.html)操作，启用[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)：

```
SET PERSIST partial_revokes = ON;
```

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

和[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)启用，部分撤销成功：

```
mysql> REVOKE INSERT ON world.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+------------------------------------------+
| Grants for u1@%                          |
+------------------------------------------+
| GRANT SELECT, INSERT ON *.* TO `u1`@`%`  |
| REVOKE INSERT ON `world`.* FROM `u1`@`%` |
+------------------------------------------+
```

[`显示赠款`](show-grants.html)将部分撤销列为[`撤销`](revoke.html)其输出中的语句。结果表明`u1`拥有全球[`选择`](privileges-provided.html#priv_select)和[`插入`](privileges-provided.html#priv_insert)特权，除了[`插入`](privileges-provided.html#priv_insert)不能对表中的表行使`世界`架构。也就是说，通过访问`u1`到`世界`表是只读的。

服务器记录通过部分撤销实现的权限限制在`mysql.user`系统表。如果一个帐户有部分撤销，它的`用户属性`列值具有`限制`属性：

```
mysql> SELECT User, Host, User_attributes->>'$.Restrictions'
       FROM mysql.user WHERE User_attributes->>'$.Restrictions' <> '';
+------+------+------------------------------------------------------+
| User | Host | User_attributes->>'$.Restrictions'                   |
+------+------+------------------------------------------------------+
| u1   | %    | [{"Database": "world", "Privileges": ["INSERT"]}] |
+------+------+------------------------------------------------------+
```

笔记

尽管可以对任何模式施加部分撤销，但对`mysql`作为防止常规帐户修改系统帐户的策略的一部分，系统模式尤其有用。看[保护系统帐户免受常规帐户的操纵](account-categories.html#protecting-system-accounts).

部分撤销操作受以下条件限制：

-   可以使用部分撤销来限制不存在的模式，但前提是全局授予撤销的特权。如果未全局授予特权，则为不存在的模式撤销它会产生错误。

-   部分撤销仅适用于架构级别。您不能对仅适用于全局的权限使用部分撤销（例如[`文件`](privileges-provided.html#priv_file)要么[`BINLOG_ADMIN`](privileges-provided.html#priv_binlog-admin))，或用于表、列或例程权限。

-   在权限分配中，启用[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)导致 MySQL 解释出现的未转义`_`和`%`架构名称中的 SQL 通配符作为文字字符，就像它们被转义为`\_`和`\%`.因为这改变了 MySQL 解释权限的方式，所以建议在安装的权限分配中避免未转义的通配符[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)可以启用。

    如前所述，模式级权限的部分撤销出现在[`显示赠款`](show-grants.html)输出为[`撤销`](revoke.html)陈述。这与如何不同[`显示赠款`](show-grants.html)表示“普通”模式级权限：

-   授予时，模式级权限由它们自己的表示[`授予`](grant.html)输出中的语句：

    ```
    mysql> CREATE USER u1;
    mysql> GRANT UPDATE ON mysql.* TO u1;
    mysql> GRANT DELETE ON world.* TO u1;
    mysql> SHOW GRANTS FOR u1;
    +---------------------------------------+
    | Grants for u1@%                       |
    +---------------------------------------+
    | GRANT USAGE ON *.* TO `u1`@`%`        |
    | GRANT UPDATE ON `mysql`.* TO `u1`@`%` |
    | GRANT DELETE ON `world`.* TO `u1`@`%` |
    +---------------------------------------+
    ```

-   撤销后，模式级权限会从输出中消失。它们不显示为[`撤销`](revoke.html)声明：

    ```
    mysql> REVOKE UPDATE ON mysql.* FROM u1;
    mysql> REVOKE DELETE ON world.* FROM u1;
    mysql> SHOW GRANTS FOR u1;
    +--------------------------------+
    | Grants for u1@%                |
    +--------------------------------+
    | GRANT USAGE ON *.* TO `u1`@`%` |
    +--------------------------------+
    ```

    当用户授予权限时，授予者对该权限的任何限制都将由被授予者继承，除非被授予者已经拥有不受限制的权限。考虑以下两个用户，其中一个拥有全局[`选择`](privileges-provided.html#priv_select)特权：


```
CREATE USER u1, u2;
GRANT SELECT ON *.* TO u2;
```

假设一个管理用户`行政`有一个全局但部分撤销的[`选择`](privileges-provided.html#priv_select)特权：

```
mysql> CREATE USER admin;
mysql> GRANT SELECT ON *.* TO admin WITH GRANT OPTION;
mysql> REVOKE SELECT ON mysql.* FROM admin;
mysql> SHOW GRANTS FOR admin;
+------------------------------------------------------+
| Grants for admin@%                                   |
+------------------------------------------------------+
| GRANT SELECT ON *.* TO `admin`@`%` WITH GRANT OPTION |
| REVOKE SELECT ON `mysql`.* FROM `admin`@`%`          |
+------------------------------------------------------+
```

如果`行政`赠款[`选择`](privileges-provided.html#priv_select)全球范围内`u1`和`u2`，结果因每个用户而异：

-   如果`行政`赠款[`选择`](privileges-provided.html#priv_select)全球范围内`u1`, 谁没有[`选择`](privileges-provided.html#priv_select)开始的特权，`u1`继承了`行政`权限限制：

    ```
    mysql> GRANT SELECT ON *.* TO u1;
    mysql> SHOW GRANTS FOR u1;
    +------------------------------------------+
    | Grants for u1@%                          |
    +------------------------------------------+
    | GRANT SELECT ON *.* TO `u1`@`%`          |
    | REVOKE SELECT ON `mysql`.* FROM `u1`@`%` |
    +------------------------------------------+
    ```

-   另一方面，`u2`已经拥有全球[`选择`](privileges-provided.html#priv_select)特权无限制。[`授予`](grant.html)只能增加被授予者的现有权限，不能减少它们，所以如果`行政`赠款[`选择`](privileges-provided.html#priv_select)全球范围内`u2`,`u2`不继承`行政`限制：

    ```
    mysql> GRANT SELECT ON *.* TO u2;
    mysql> SHOW GRANTS FOR u2;
    +---------------------------------+
    | Grants for u2@%                 |
    +---------------------------------+
    | GRANT SELECT ON *.* TO `u2`@`%` |
    +---------------------------------+
    ```

    如果一个[`授予`](grant.html)声明包括一个`作为 *`用户`*`子句，应用的权限限制是对子句指定的用户/角色组合的权限限制，而不是对执行语句的用户的权限限制。有关信息`作为`子句，见[第 13.7.1.6 节，“GRANT 声明”](grant.html).

    对授予帐户的新权限的限制将添加到该帐户的任何现有限制中：


```
mysql> CREATE USER u1;
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO u1;
mysql> REVOKE INSERT ON mysql.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+---------------------------------------------------------+
| Grants for u1@%                                         |
+---------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%` |
| REVOKE INSERT ON `mysql`.* FROM `u1`@`%`                |
+---------------------------------------------------------+
mysql> REVOKE DELETE, UPDATE ON db2.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+---------------------------------------------------------+
| Grants for u1@%                                         |
+---------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%` |
| REVOKE UPDATE, DELETE ON `db2`.* FROM `u1`@`%`          |
| REVOKE INSERT ON `mysql`.* FROM `u1`@`%`                |
+---------------------------------------------------------+
```

特权限制的聚合适用于显式部分撤销特权（如图所示）以及从执行语句的用户或在`作为 *`用户`*`条款。

如果帐户对架构有权限限制：

-   该帐户不能向其他帐户授予对受限模式或其中任何对象的权限。

-   另一个没有限制的帐户可以为受限制的帐户授予受限制的架构或其中的对象的权限。假设一个不受限制的用户执行这些语句：

    ```
    CREATE USER u1;
    GRANT SELECT, INSERT, UPDATE ON *.* TO u1;
    REVOKE SELECT, INSERT, UPDATE ON mysql.* FROM u1;
    GRANT SELECT ON mysql.user TO u1;          -- grant table privilege
    GRANT SELECT(Host,User) ON mysql.db TO u1; -- grant column privileges
    ```

    生成的帐户具有以下权限，能够在受限模式中执行受限操作：

    ```
    mysql> SHOW GRANTS FOR u1;
    +-----------------------------------------------------------+
    | Grants for u1@%                                           |
    +-----------------------------------------------------------+
    | GRANT SELECT, INSERT, UPDATE ON *.* TO `u1`@`%`           |
    | REVOKE SELECT, INSERT, UPDATE ON `mysql`.* FROM `u1`@`%`  |
    | GRANT SELECT (`Host`, `User`) ON `mysql`.`db` TO `u1`@`%` |
    | GRANT SELECT ON `mysql`.`user` TO `u1`@`%`                |
    +-----------------------------------------------------------+
    ```

    如果帐户对全局权限有限制，则可以通过以下任何操作删除该限制：

-   通过对权限没有限制的帐户全局授予该帐户权限。

-   在架构级别授予权限。

-   全局撤销权限。

    考虑一个用户`u1`谁在全球范围内拥有多项特权，但受到限制[`插入`](privileges-provided.html#priv_insert),[`更新`](privileges-provided.html#priv_update)和[`删除`](privileges-provided.html#priv_delete)：


```
mysql> CREATE USER u1;
mysql> GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO u1;
mysql> REVOKE INSERT, UPDATE, DELETE ON mysql.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+----------------------------------------------------------+
| Grants for u1@%                                          |
+----------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%`  |
| REVOKE INSERT, UPDATE, DELETE ON `mysql`.* FROM `u1`@`%` |
+----------------------------------------------------------+
```

全局授予权限`u1`从没有限制的帐户删除权限限制。例如，要删除[`插入`](privileges-provided.html#priv_insert)限制：

```
mysql> GRANT INSERT ON *.* TO u1;
mysql> SHOW GRANTS FOR u1;
+---------------------------------------------------------+
| Grants for u1@%                                         |
+---------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%` |
| REVOKE UPDATE, DELETE ON `mysql`.* FROM `u1`@`%`        |
+---------------------------------------------------------+
```

在架构级别授予权限以`u1`删除权限限制。例如，要删除[`更新`](privileges-provided.html#priv_update)限制：

```
mysql> GRANT UPDATE ON mysql.* TO u1;
mysql> SHOW GRANTS FOR u1;
+---------------------------------------------------------+
| Grants for u1@%                                         |
+---------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO `u1`@`%` |
| REVOKE DELETE ON `mysql`.* FROM `u1`@`%`                |
+---------------------------------------------------------+
```

撤销全局权限会删除该权限，包括对其的任何限制。例如，要删除[`删除`](privileges-provided.html#priv_delete)限制（以删除所有[`删除`](privileges-provided.html#priv_delete)使用权）：

```
mysql> REVOKE DELETE ON *.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+-------------------------------------------------+
| Grants for u1@%                                 |
+-------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE ON *.* TO `u1`@`%` |
+-------------------------------------------------+
```

如果一个帐户在全局和架构级别都具有特权，您必须在架构级别撤销它两次以实现部分撤销。假设`u1`拥有这些特权，其中[`插入`](privileges-provided.html#priv_insert)在全球范围内和`世界`架构：

```
mysql> CREATE USER u1;
mysql> GRANT SELECT, INSERT ON *.* TO u1;
mysql> GRANT INSERT ON world.* TO u1;
mysql> SHOW GRANTS FOR u1;
+-----------------------------------------+
| Grants for u1@%                         |
+-----------------------------------------+
| GRANT SELECT, INSERT ON *.* TO `u1`@`%` |
| GRANT INSERT ON `world`.* TO `u1`@`%`   |
+-----------------------------------------+
```

撤销[`插入`](privileges-provided.html#priv_insert)在`世界`撤销架构级权限 ([`显示赠款`](show-grants.html)不再显示架构级别[`授予`](grant.html)陈述）：

```
mysql> REVOKE INSERT ON world.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+-----------------------------------------+
| Grants for u1@%                         |
+-----------------------------------------+
| GRANT SELECT, INSERT ON *.* TO `u1`@`%` |
+-----------------------------------------+
```

撤销[`插入`](privileges-provided.html#priv_insert)在`世界`再次执行部分撤销全局特权（[`显示赠款`](show-grants.html)现在包括架构级别[`撤销`](revoke.html)陈述）：

```
mysql> REVOKE INSERT ON world.* FROM u1;
mysql> SHOW GRANTS FOR u1;
+------------------------------------------+
| Grants for u1@%                          |
+------------------------------------------+
| GRANT SELECT, INSERT ON *.* TO `u1`@`%`  |
| REVOKE INSERT ON `world`.* FROM `u1`@`%` |
+------------------------------------------+
```

#### 部分撤销与显式模式授权

为了提供对某些模式而不是其他模式的帐户的访问权限，部分撤销提供了一种替代方法，即显式授予模式级访问权限而不授予全局权限。这两种方法有不同的优点和缺点。

授予架构级权限而不是全局权限：

-   添加新架构：默认情况下，现有帐户无法访问架构。对于架构应该可以访问的任何帐户，DBA 必须授予架构级别的访问权限。

-   添加新帐户：DBA 必须为帐户应有权访问的每个模式授予模式级访问权限。

    与部分撤销一起授予全局权限：

-   添加新架构：具有全局权限的现有帐户可以访问该架构。对于架构应该无法访问的任何此类帐户，DBA 必须添加部分撤销。

-   添加新帐户：DBA 必须授予全局权限，以及对每个受限模式的部分撤销。

    使用显式模式级授权的方法对于访问权限仅限于少数模式的帐户更方便。使用部分撤销的方法对于可以广泛访问除少数模式之外的所有模式的帐户更方便。

#### 禁用部分撤销

启用后，[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)如果任何帐户有权限限制，则无法禁用。如果存在任何此类帐户，则禁用[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)失败：

-   对于尝试禁用[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)在启动时，服务器会记录一条错误消息并启用[`部分撤销`](server-system-variables.html#sysvar_partial_revokes).

-   对于尝试禁用[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)在运行时，会发生错误并且[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)值保持不变。

    禁用[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)当存在限制时，首先必须删除限制：

1.  确定哪些账户有部分撤销：

    ```
    SELECT User, Host, User_attributes->>'$.Restrictions'
    FROM mysql.user WHERE User_attributes->>'$.Restrictions' <> '';
    ```

2.  对于每个此类帐户，删除其权限限制。假设上一步显示 account`u1`有这些限制：

    ```
    [{"Database": "world", "Privileges": ["INSERT", "DELETE"]
    ```

    可以通过多种方式解除限制：

    -   全局授予权限，不受限制：

        ```
        GRANT INSERT, DELETE ON *.* TO u1;
        ```

    -   在架构级别授予权限：

        ```
        GRANT INSERT, DELETE ON world.* TO u1;
        ```

    -   全局撤销权限（假设不再需要）：

        ```
        REVOKE INSERT, DELETE ON *.* FROM u1;
        ```

    -   删除帐户本身（假设不再需要）：

        ```
        DROP USER u1;
        ```

    删除所有权限限制后，可以禁用部分撤销：


```
SET PERSIST partial_revokes = OFF;
```

#### 部分撤销和复制

在复制场景中，如果[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)在任何主机上启用，它必须在所有主机上启用。否则，[`撤销`](revoke.html)部分撤销全局特权的语句对发生复制的所有主机的效果不同，可能导致复制不一致或错误。

什么时候[`部分撤销`](server-system-variables.html#sysvar_partial_revokes)启用后，扩展语法会记录在二进制日志中[`授予`](grant.html)语句，包括发出语句的当前用户及其当前活动的角色。如果以这种方式记录的用户或角色在副本上不存在，则复制应用程序线程停止在[`授予`](grant.html)有错误的声明。确保所有发出或可能发出的用户帐户[`授予`](grant.html)复制源服务器上的语句也存在于副本上，并且具有与源上相同的角色集。
