MySQL列级权限仅支持SELECT/INSERT/UPDATE/REFERENCES,DELETE等行级操作不支持列粒度;授权时列名错误不报错但查询会失败;UPDATE需授权WHERE中所有列;列权限与表权限共存时后者覆盖前者。

列级权限只支持 SELECT/INSERT/UPDATE,DELETE 不行
MySQL 的列级权限不是“所有操作都能按列控制”,它只对 SELECT、INSERT、UPDATE、REFERENCES 四种操作生效。像 DELETE、TRUNCATE、ALTER 这类天然作用于整行的操作,压根不支持列粒度——你写 GRANT DELETE (id) ON db.tbl TO 'u'@'h',MySQL 会默默忽略括号里的列名,降级成表级 DELETE 权限(如果用户本来就有),或者直接报语法错(取决于版本)。所以别白费劲去试列级 DELETE,真要限制删数据,得靠视图、存储过程封装,或从应用层拦截。
授权语句写错列名,不会报错,但查询直接崩
这是最常踩的坑:执行 GRANT SELECT (name, email) ON test.users TO 'reader'@'%',如果 test.users 表里根本没有 email 这一列,MySQL 不会拒绝授权,也不会警告,语句照样返回 Query OK。但之后用户一查 SELECT email FROM test.users,得到的不是权限错误,而是赤裸裸的 Unknown column 'email' in 'field list'。你以为是权限没给对,其实只是列名拼错了。
- 授权前先确认列存在:
DESCRIBE test.users或SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='test' AND TABLE_NAME='users' - 注意大小写:列名是否区分大小写,取决于系统变量
lower_case_table_names的设置,别凭感觉猜 - 别靠报错反推权限问题——先查结构,再查权限
UPDATE 列权限比你想的更严格
UPDATE 列权限不是“只允许改这些字段”那么简单。它要求整个语句涉及的所有列(包括 WHERE 子句里用到的列)都必须在授权范围内。比如你只授了 GRANT UPDATE (status) ON app.orders TO 'worker'@'10.0.1.%',那用户连 UPDATE app.orders SET status = 'done' WHERE id = 123 都执行不了,因为 WHERE id = 123 引用了未授权的 id 列,直接报 ERROR 1142 (42000): UPDATE command denied to user。
- 真正可用的写法是:把
WHERE条件里用到的列也加进授权列表,例如GRANT UPDATE (status, id) ON app.orders TO 'worker'@'10.0.1.%' - 或者换思路:用视图屏蔽敏感列,再对视图授
UPDATE权限,避免暴露主键或业务逻辑字段
列权限和表权限共存时,“并集”不等于“叠加”
MySQL 对权限的检查是“只要有一处允许,就放行”,但列级权限实际起的是“过滤器”作用。比如你先执行 GRANT SELECT ON myapp.users TO 'u'@'h'(全表可查),再执行 GRANT SELECT (name, email) ON myapp.users TO 'u'@'h',最终效果不是“既能查全表又能只查两列”,而是后者覆盖前者——用户只能查 name 和 email,其他列查不到值(SELECT * 返回空值或 NULL,具体行为取决于 MySQL 版本和 SQL mode)。
- 撤权限也要匹配粒度:
REVOKE SELECT (name, email) ON myapp.users FROM 'u'@'h'才能去掉列级限制;用REVOKE SELECT ON myapp.users是撤不掉的 - 查当前列权限,看
mysql.columns_priv表,重点看is_active = 'Y'字段,别只盯着SHOW GRANTS输出——它有时不显示列级细节










