MySQL列级权限需在GRANT中显式列出列名,如GRANT SELECT (id, name) ON db.t1 TO 'u'@'localhost';通配符*无效,且列列表必须紧邻权限名后、ON前;表级与列级权限独立管理,撤销表级权限不影响已授列级权限。

GRANT 语句里怎么指定列级权限
MySQL 的列级权限只能在 GRANT 时显式列出字段,不能通配(比如 * 不适用于列)。你得把要授权的列名一个不落地写进括号里。
常见错误是以为 SELECT 权限默认覆盖所有列,其实只要没列出来,哪怕有表级 SELECT,查那几列也会报 ERROR 1142 (42000): SELECT command denied to user。
-
GRANT SELECT (id, name) ON db.t1 TO 'u'@'localhost';—— 正确:只放行 id 和 name 列 -
GRANT SELECT ON db.t1 (id, name) TO 'u'@'localhost';—— 错误:语法非法,列列表必须紧贴在权限名后、ON前 - 如果同时需要表级和列级权限,得拆成两条
GRANT:一条给表(如INSERT),一条给列(如SELECT (a,b))
REVOKE 时列权限不会自动继承表级撤销
撤销表级权限(比如 REVOKE SELECT ON db.t1 FROM 'u'@'localhost')不会连带清掉之前单独授过的列级 SELECT。MySQL 把它们当独立权限项存。
这意味着:用户可能仍能查某些列,即使你已经“撤了表的 SELECT”。实际权限是“表级 + 所有已授列级”的并集。
- 查当前用户具体有哪些列权限?用
SHOW GRANTS FOR 'u'@'localhost';,注意输出里会出现类似GRANT SELECT (col1, col2) ON `db`.`t1` TO ...的行 - 要彻底清理列权限,必须显式
REVOKE SELECT (col1, col2) ON db.t1 FROM 'u'@'localhost'; - 列权限无法通过
FLUSH PRIVILEGES清除,它只刷新内存缓存,不修改权限元数据
WHERE 条件里引用未授权列会直接报错
哪怕只是在 WHERE 或 ORDER BY 里用到一个没被授权的列,查询也会失败。MySQL 在解析阶段就校验列权限,不等执行。
例如:用户只有 SELECT (name) 权限,却执行 SELECT name FROM t1 WHERE id = 1;,会报 ERROR 1142 (42000): SELECT command denied to user ... for column 'id'。
- 解决办法不是加
SELECT *,而是确保WHERE/ORDER BY/GROUP BY涉及的所有列都在授权列表中 - 视图(
VIEW)可以绕过部分限制——定义视图时用的是定义者权限,但查询视图时仍受调用者列权限约束 - 触发器或存储过程里访问未授权列,同样触发权限检查,且错误发生在执行时而非创建时
mysql.user 表不存列级权限,得看 mysql.columns_priv
表级权限存在 mysql.tables_priv,列级权限则单独存在 mysql.columns_priv。直接查 mysql.user 或 mysql.db 看不到列授权痕迹。
这导致两个实际问题:备份还原后列权限丢失(因为 mysqldump --all-databases 默认不导 mysql 库)、用 pt-show-grants 工具可能漏掉列级授权。
- 导出列权限:手动
SELECT * FROM mysql.columns_priv;,或用mysqldump mysql columns_priv - 权限生效依赖
host/db/user/table_name/column_name五元组完全匹配,大小写敏感(取决于文件系统) -
column_name字段存的是二进制值,直接 SELECT 可能显示乱码;用CONVERT(column_name USING utf8mb4)查看可读名
列级权限的粒度很细,但代价是维护成本高——每增一列就得改一次 GRANT,而且容易漏掉 WHERE 或排序字段这种“隐性依赖”。真正要用,建议先在测试库跑通 SHOW GRANTS 和真实查询报错路径。


















