mysql不支持列级权限,必须通过创建显式字段列表的安全视图并授予视图select权限来实现列级访问控制,同时严格禁止用户对基表的任何直接权限。

MySQL 没有原生列级权限,必须用视图绕过
MySQL 8.0 及之前所有版本都不支持 GRANT SELECT(col1, col2) 这类列级授权语法。直接对表执行 GRANT SELECT ON db.tbl TO 'u'@'%',用户拿到的是整张表的读权限;想隐藏某些字段(比如 salary、id_card),唯一可靠路径是建视图,再给用户授视图权限。
常见错误现象:ERROR 1142 (42000): SELECT command denied to user 'u'@'%' for table 'tbl' —— 这往往是因为用户只被授予了视图权限,却试图直连原表;或者反过来,用户有原表权限,但视图定义里用了用户无权访问的字段,导致视图查询失败。
- 视图必须由有原表完整
SELECT权限的账号创建(通常是root或 DBA) - 视图权限和基表权限完全独立:即使用户对原表无任何权限,只要被授予视图
SELECT,就能查视图 - 视图不能包含子查询、临时表、用户变量等非确定性结构,否则可能在某些 MySQL 版本中无法被授权
创建安全视图时,显式列出字段比 SELECT * 更可靠
用 SELECT * 创建视图看似省事,但一旦基表新增敏感字段(比如加了个 password_hash),视图会自动包含它,而你可能根本没意识到权限已泄露。显式写字段名,等于做了白名单控制。
使用场景:HR 系统中,普通员工只能看自己的 name、dept、join_date,不能看 salary、manager_id。
CREATE VIEW emp_public AS SELECT id, name, dept, join_date FROM employees WHERE id = CURRENT_USER(); -- 可选:配合行级过滤
- 字段顺序不重要,但别漏掉
WHERE中引用的字段(比如上例的id) - 如果视图含
WHERE条件,确保条件字段也在SELECT列表里,否则部分客户端(如某些 ORM)可能报错 - 避免在视图中用函数封装敏感字段(如
SHA2(salary, 256)),这属于应用层混淆,不是权限隔离
授权时只给视图权限,且禁用原表访问
权限分配必须严格分层:用户账号只拥有视图的 SELECT,对原表不做任何授权。否则视图形同虚设——用户只要换条 SQL 就能绕过去。
性能影响极小:MySQL 视图是“虚拟表”,查询时会重写为对基表的语句并优化执行,没有额外 IO 或缓存开销。
- 执行
GRANT SELECT ON mydb.emp_public TO 'staff'@'%'; - 确认原表无残留权限:
SHOW GRANTS FOR 'staff'@'%';—— 输出里不应出现employees或* - 测试时用目标账号登录,执行
SELECT * FROM employees;应报错;SELECT * FROM emp_public;应成功
注意视图 DEFINER 和 SQL SECURITY 的隐含风险
视图默认以 DEFINER = 'root'@'%' 创建,执行时按定义者权限检查。这本身是必要设计,但若定义者账号密码泄露或权限被提升,攻击者可通过视图间接读取高权限数据。更稳妥的做法是显式声明 SQL SECURITY DEFINER 并确保定义者账号最小化权限。
容易踩的坑:SQL SECURITY INVOKER 看似更安全,但它会让视图执行时检查调用者的权限,而调用者本就没有原表权限——结果就是视图查不了,直接报错。
- 创建时加上
SQL SECURITY DEFINER(MySQL 8.0 默认,但低版本建议显式写) - 定义者账号(如
definer_user)只需对基表有SELECT,不要给DROP、UPDATE等多余权限 - 定期审计
mysql.proc或用SHOW CREATE VIEW emp_public;检查定义者是否仍是预期账号
真正麻烦的是跨库视图和触发器联动场景——这时权限链会变长,一次疏忽就可能让列隔离失效。别指望靠注释或文档来守门,得靠权限语句本身卡死。











