mysql原生不支持行级权限,因sql标准授权模型无此粒度;唯一可靠方案是sql security definer视图配合严格权限隔离,漏授原表权限或误配安全上下文即失效。

MySQL 原生不支持行级权限,不是因为“没做”,而是 SQL 标准授权模型里压根没这层粒度;所有替代方案都必须绕过 GRANT 语法,靠视图 + 权限隔离闭环实现,漏掉任一环节就等于没控制。
GRANT 语句根本不识别 WHERE 条件
执行 GRANT SELECT ON mydb.orders TO 'alice'@'%',MySQL 只检查你有没有表级权限,不关心你查哪几行。哪怕你在应用里拼 WHERE user_id = 123,数据库也只当它是查询的一部分,不参与授权判定。
常见错误现象:ERROR 1064 (42000): You have an error in your SQL syntax——当你试图写 GRANT SELECT ON mydb.orders WHERE user_id = CURRENT_USER(),MySQL 直接报语法错,因为这个语法根本不存在。
- MySQL 的权限检查发生在查询解析阶段,不是执行阶段
- MyISAM 和 InnoDB 在这点上完全一致:引擎不决定权限,Server 层的 GRANT 模型决定
- 所谓“InnoDB 支持行级锁”和“行级权限”毫无关系,锁是并发控制,权限是访问控制
SQL SECURITY DEFINER 视图是唯一可靠路径
普通视图(SQL SECURITY INVOKER)只是查询别名,用户仍可直连原表或用子查询穿透;必须用 SQL SECURITY DEFINER,让视图以高权限账户身份执行过滤逻辑。
正确建法示例:
CREATE DEFINER='admin'@'localhost' SQL SECURITY DEFINER VIEW user_orders AS SELECT * FROM orders WHERE user_id = get_current_user_id();
-
DEFINER='admin'@'localhost'账号必须对orders和auth_mapping都有SELECT权限 - 不能用
CURRENT_USER()或USER()直接比较——前者返回'app@localhost',后者在连接池下全一样 - 必须用存储函数(如
get_current_user_id())封装映射逻辑,查auth_mapping表把连接名转成业务 ID
权限闭环比视图定义更关键
再严的视图定义,只要用户还能执行 SELECT * FROM orders,整套机制就失效。这不是“建议”,是硬性前提。
- 立刻执行:
REVOKE SELECT ON mydb.orders FROM 'alice'@'%' - 只授视图:
GRANT SELECT ON mydb.user_orders TO 'alice'@'%' - 验证是否干净:
SHOW GRANTS FOR 'alice'@'%'—— 输出里不能出现orders、mydb.*或通配符 - 如果视图涉及 JOIN 多张表,
DEFINER账户必须对每张基表都有对应权限,缺一张就会报ERROR 1449
真正容易被忽略的是映射表的维护成本和函数调用的性能开销:每次查视图都要走一次 auth_mapping 查询,若没加索引或缓存,高并发下会成为瓶颈;而一旦管理员手抖多授了一次原表权限,所有过滤逻辑瞬间归零。











