Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions TOC.md
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,7 @@
- 权限
- [与 MySQL 安全特性差异](/security-compatibility-with-mysql.md)
- [权限管理](/privilege-management.md)
- [列级权限管理](/column-privilege-management.md)
- [TiDB 用户账户管理](/user-account-management.md)
- [TiDB 密码管理](/password-management.md)
- [基于角色的访问控制](/role-based-access-control.md)
Expand Down
178 changes: 178 additions & 0 deletions column-privilege-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
---
title: 列级权限管理
summary: TiDB 支持兼容 MySQL 的列级权限管理机制,可通过 GRANT/REVOKE 在表级别对指定列授予或回收 SELECT、INSERT、UPDATE、REFERENCES 权限,实现更细粒度的访问控制。
---

# 列级权限管理

从 v8.5.6 版本开始,TiDB 支持兼容 MySQL 的列级权限管理机制。通过列级权限,你可以在表级别为指定列授予或回收 `SELECT`、`INSERT`、`UPDATE`、`REFERENCES` 权限,从而实现更细粒度的数据访问控制。

> **注意:**
>
> 虽然 MySQL 语法允许 `REFERENCES(col_name)` 这种列级写法,但 `REFERENCES` 本身属于数据库/表级权限(用于外键相关的权限检查)。因此,列级 `REFERENCES` 在 MySQL 中并不会真正生效,TiDB 与 MySQL 保持一致。

## 语法

列级权限的授予和回收与表级权限类似。不同之处在于:列名列表跟在权限类型后面,而不是跟在表名后面;多个列名之间使用逗号(`,`)分隔。

{{< copyable "sql" >}}

```sql
GRANT priv_type(col_name [, col_name] ...) [, priv_type(col_name [, col_name] ...)] ...
ON db_name.tbl_name
TO 'user'@'host';

REVOKE priv_type(col_name [, col_name] ...) [, priv_type(col_name [, col_name] ...)] ...
ON db_name.tbl_name
FROM 'user'@'host';
```

其中:

* `priv_type` 支持 `SELECT`、`INSERT`、`UPDATE` 和 `REFERENCES`。
* `ON` 后需要指定具体表,例如 `test.tbl`。
* 同一条 `GRANT` 或 `REVOKE` 语句可以包含多个权限项,每个权限项都可以指定自己的列名列表。

例如,以下语句表示将 `col1`、`col2` 的 `SELECT` 权限和 `col3` 的 `UPDATE` 权限授予用户:

{{< copyable "sql" >}}

```sql
GRANT SELECT(col1, col2), UPDATE(col3) ON test.tbl TO 'user'@'host';
```

## 授予列级权限

以下示例将表 `test.tbl` 的 `col1` 和 `col2` 的 `SELECT` 权限授予用户 `newuser`,并将 `col3` 的 `UPDATE` 权限授予该用户:

{{< copyable "sql" >}}

```sql
CREATE DATABASE IF NOT EXISTS test;
USE test;

DROP TABLE IF EXISTS tbl;
CREATE TABLE tbl (col1 INT, col2 INT, col3 INT);

DROP USER IF EXISTS 'newuser'@'%';
CREATE USER 'newuser'@'%';

GRANT SELECT(col1, col2), UPDATE(col3) ON test.tbl TO 'newuser'@'%';
SHOW GRANTS FOR 'newuser'@'%';
```

```
+---------------------------------------------------------------------+
| Grants for newuser@% |
+---------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'newuser'@'%' |
| GRANT SELECT(col1, col2), UPDATE(col3) ON test.tbl TO 'newuser'@'%' |
+---------------------------------------------------------------------+
```

除了使用 `SHOW GRANTS`,你还可以通过查询 `INFORMATION_SCHEMA.COLUMN_PRIVILEGES` 查看列级权限信息。

## 回收列级权限

以下示例从用户 `newuser` 收回列 `col2` 的 `SELECT` 权限:

{{< copyable "sql" >}}

```sql
REVOKE SELECT(col2) ON test.tbl FROM 'newuser'@'%';
SHOW GRANTS FOR 'newuser'@'%';
```

```
+---------------------------------------------------------------+
| Grants for newuser@% |
+---------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'newuser'@'%' |
| GRANT SELECT(col1), UPDATE(col3) ON test.tbl TO 'newuser'@'%' |
+---------------------------------------------------------------+
```

## 列级权限访问控制示例

在授予或回收列级权限后,TiDB 会对 SQL 中引用的列进行权限检查。例如:

* `SELECT` 语句:`SELECT` 列权限会影响 `SELECT` 列表以及 `WHERE`、`ORDER BY` 等子句中引用的列。
* `UPDATE` 语句:`SET` 中被更新的列需要 `UPDATE` 列权限;在表达式、条件中被读取的列通常还需要 `SELECT` 列权限。
* `INSERT` 语句:被写入的列需要 `INSERT` 列权限(`INSERT INTO t VALUES (...)` 等价于写入所有列)。

以下示例中,用户 `newuser` 仅能查询 `col1`,并更新 `col3`:

{{< copyable "sql" >}}

```sql
-- 以 newuser 登录执行
SELECT col1 FROM tbl;
SELECT * FROM tbl; -- 报错(缺少 col2、col3 的 SELECT 列权限)

UPDATE tbl SET col3 = 1;
UPDATE tbl SET col1 = 2; -- 报错(缺少 col1 的 UPDATE 列权限)

UPDATE tbl SET col3 = col1;
UPDATE tbl SET col3 = col3 + 1; -- 报错(缺少 col3 的 SELECT 列权限)
UPDATE tbl SET col3 = col1 WHERE col1 > 0;
```

## 与 MySQL 的兼容性差异

TiDB 的列级权限整体与 MySQL 兼容,但在以下场景存在差异:

| 场景 | TiDB | MySQL |
| :-- | :-- | :-- |
| 收回用户未被授予的列级权限 | `REVOKE` 可以成功执行 | `REVOKE` 会报错 |
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个场景下,似乎不能说明 TiDB 检查更加严格。建议直接说差异即可。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All fixed, PTAL again

| 列裁剪与 `SELECT` 列权限检查的执行顺序 | 先检查 `SELECT` 列权限,再进行列裁剪。例如:执行 `SELECT a FROM (SELECT a, b FROM t) s` 需要同时拥有 `t.a` 和 `t.b` 的 `SELECT` 列权限。 | 先进行列裁剪,再检查 `SELECT` 列权限。例如:执行 `SELECT a FROM (SELECT a, b FROM t) s` 只需要 `t.a` 的 `SELECT` 列权限。 |

## 附录:视图场景的列裁剪与权限检查

在对视图进行 `SELECT` 权限检查时,MySQL 会先对视图内部查询做列裁剪,再检查内部表的列权限,因此在某些场景下检查相对宽松。TiDB 不会在权限检查之前做列裁剪,因此可能需要额外的列权限。

{{< copyable "sql" >}}

```sql
-- 以 root 登录准备环境
DROP USER IF EXISTS 'u'@'%';
CREATE USER 'u'@'%';

DROP TABLE IF EXISTS t;
CREATE TABLE t (a INT, b INT, c INT, d INT);

DROP VIEW IF EXISTS v;
CREATE SQL SECURITY INVOKER VIEW v AS SELECT a, b FROM t WHERE c = 0 ORDER BY d;

GRANT SELECT ON v TO 'u'@'%';

-- 以 u 登录
SELECT a FROM v;
-- MySQL:报错,缺少对 t.a、t.c、t.d 的访问权限
-- TiDB:报错,缺少对 t.a、t.b、t.c、t.d 的访问权限

-- 以 root 登录
GRANT SELECT(a, c, d) ON t TO 'u'@'%';

-- 以 u 登录
SELECT a FROM v;
-- MySQL:成功(会将内部查询裁剪为 `SELECT a FROM t WHERE c = 0 ORDER BY d`)
-- TiDB:报错,缺少对 t.b 的访问权限

SELECT * FROM v;
-- MySQL:报错,缺少对 t.b 的访问权限
-- TiDB:报错,缺少对 t.b 的访问权限

-- 以 root 登录
GRANT SELECT(b) ON t TO 'u'@'%';

-- 以 u 登录
SELECT * FROM v;
-- MySQL:成功
-- TiDB:成功
```

## 另请参阅

* [权限管理](/privilege-management.md)
* [`GRANT <privileges>`](/sql-statements/sql-statement-grant-privileges.md)
* [`REVOKE <privileges>`](/sql-statements/sql-statement-revoke-privileges.md)
1 change: 0 additions & 1 deletion mysql-compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ TiDB 高度兼容 MySQL 协议,以及 MySQL 5.7 和 MySQL 8.0 常用的功能
* MySQL 追踪优化器
* XML 函数
* X-Protocol [#1109](https://github.com/pingcap/tidb/issues/1109)
* 列级权限 [#9766](https://github.com/pingcap/tidb/issues/9766)
* `XA` 语法(TiDB 内部使用两阶段提交,但并没有通过 SQL 接口公开)
* `CREATE TABLE tblName AS SELECT stmt` 语法 [#4754](https://github.com/pingcap/tidb/issues/4754)
* `CHECK TABLE` 语法 [#4673](https://github.com/pingcap/tidb/issues/4673)
Expand Down
2 changes: 2 additions & 0 deletions privilege-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ GRANT SELECT ON test.* TO 'xxx'@'%';
GRANT ALL PRIVILEGES ON *.* TO 'xxx'@'%';
```

从 v8.5.6 版本开始,TiDB 支持兼容 MySQL 的列级权限管理机制。你可以在表级别为指定列授予或回收 `SELECT`、`INSERT`、`UPDATE`、`REFERENCES` 权限。更多信息参见[列级权限管理](/column-privilege-management.md)。

默认情况下,如果指定的用户不存在,[`GRANT`](/sql-statements/sql-statement-grant-privileges.md) 语句将报错。该行为受 [SQL 模式](/system-variables.md#sql_mode)中的 `NO_AUTO_CREATE_USER` 控制。

```sql
Expand Down
2 changes: 1 addition & 1 deletion sql-statements/sql-statement-grant-privileges.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ SHOW GRANTS FOR 'newuser';
## MySQL 兼容性

* 与 MySQL 类似,`USAGE` 权限表示登录 TiDB 服务器的能力。
* 目前不支持列级权限
* 在 TiDB v8.5.6 及后续 8.5.x 版本中,支持兼容 MySQL 的列级权限管理机制。你可以在表级别为指定列授予或回收 `SELECT`、`INSERT`、`UPDATE`、`REFERENCES` 权限,详见[列级权限管理](/column-privilege-management.md)
* 与 MySQL 类似,不存在 `NO_AUTO_CREATE_USER` sql 模式时,`GRANT` 语句将在用户不存在时自动创建一个空密码的新用户。删除此 sql-mode(默认情况下已启用)会带来安全风险。
* `GRANT <privileges>` 语句执行成功后,在 TiDB 中语句执行的结果会在当前连接立即生效,而 [MySQL 中部分权限的结果需要等到之后的连接才生效](https://dev.mysql.com/doc/refman/8.0/en/privilege-changes.html)。见 [TiDB #39356](https://github.com/pingcap/tidb/issues/39356)。

Expand Down
2 changes: 2 additions & 0 deletions sql-statements/sql-statement-revoke-privileges.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ summary: TiDB 数据库中 REVOKE <privileges> 的使用概况。

`REVOKE <privileges>` 语句用于删除已有用户的权限。执行 `REVOKE <privileges>` 语句需要拥有分配的权限,并且拥有 `GRANT OPTION` 权限。

在 TiDB v8.5.6 及后续 8.5.x 版本中,支持兼容 MySQL 的列级权限管理机制,你可以在 `REVOKE` 中指定列名列表,例如 `REVOKE SELECT(col2) ON test.tbl FROM 'user'@'host';`。更多信息参见[列级权限管理](/column-privilege-management.md)。

## 语法图

```ebnf+diagram
Expand Down
Loading