PingKai Logo下载

列级权限管理

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

语法

列级权限的授予和回收语法与表级权限类似,区别如下:

  • 列名列表写在权限类型后面,而不是写在表名后面。
  • 多个列名之间使用逗号(,)分隔。
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 支持 SELECTINSERTUPDATEREFERENCES
  • ON 后必须指定具体表,例如 test.tbl
  • 同一条 GRANTREVOKE 语句可以包含多个权限项,每个权限项都可以指定自己的列名列表。

例如,以下语句表示将 col1col2SELECT 权限和 col3UPDATE 权限授予用户:

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

授予列级权限示例

以下示例将表 test.tblcol1col2SELECT 权限授予用户 newuser,并将 col3UPDATE 权限授予该用户:

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 收回列 col2SELECT 权限:

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 列表以及 WHEREORDER BY 等子句中引用的列。
  • UPDATE 语句:SET 子句中被更新的列需要 UPDATE 列权限。在表达式、条件中被读取的列通常还需要 SELECT 列权限。
  • INSERT 语句:被写入的列需要 INSERT 列权限。INSERT INTO t VALUES (...) 等价于按表定义顺序向所有列写入值。

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

-- 以 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 兼容,但在以下场景存在差异:

场景TiDBMySQL
收回用户未被授予的列级权限REVOKE 可以成功执行在未使用 IF EXISTS 时,REVOKE 会报错
列裁剪与 SELECT 列权限检查的执行顺序先检查 SELECT 列权限,再进行列裁剪。例如,执行 SELECT a FROM (SELECT a, b FROM t) s 需要同时拥有 t.at.bSELECT 列权限。先进行列裁剪,再检查 SELECT 列权限。例如,执行 SELECT a FROM (SELECT a, b FROM t) s 只需要 t.aSELECT 列权限。

视图场景的列裁剪与权限检查

在对视图进行 SELECT 权限检查时,MySQL 和 TiDB 存在以下差异:

  • MySQL 会先对视图内部查询做列裁剪,再检查内部表的列权限,因此在某些场景下检查相对宽松。
  • TiDB 不会在权限检查之前做列裁剪,因此可能需要额外的列权限。
-- 以 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:成功

另请参阅