MySQL原生不支持行级权限,需通过SQL SECURITY DEFINER视图、存储函数和权限闭环协同实现;USER()不可靠,须用DEFINER函数封装映射逻辑,并严格管控底层表与映射表权限。

MySQL 原生不支持 GRANT SELECT ON table WHERE 这类行级权限语法,所谓“细粒度行级安全”必须靠 SQL SECURITY DEFINER 视图 + 存储函数 + 权限闭环三者协同实现,缺一不可。
为什么直接用 USER() 或 CURRENT_USER() 做视图过滤会失效
常见错误现象:用户查视图返回空结果,或所有用户看到同一组数据。
-
USER()返回的是连接凭据名(如'app_pool@10.20.5.10'),不是业务系统里的user_id;JWT 或 Session 中的sub根本不在数据库连接上下文中 - 连接池场景下(如 ProxySQL、HikariCP),所有请求都显示为同一个
USER()(如'webapp'@'localhost'),彻底失去区分能力 - 用户名含
@字符时(如'alice@prod'@'192.168.1.1'),SUBSTRING_INDEX(USER(), '@', 1)会截断出错,得到'alice'而非完整用户名 - 若
user_id是整型字段,而解析出的字符串参与比较,MySQL 8.0+ 的STRICT_TRANS_TABLES会触发类型转换失败,整个查询中断并报错
必须用 SQL SECURITY DEFINER 存储函数封装用户映射逻辑
把“谁在查”从脆弱的字符串解析升级为可维护、可测试的逻辑层。
- 先建映射表:
CREATE TABLE auth_mapping (conn_user VARCHAR(100) PRIMARY KEY, business_user_id INT NOT NULL); - 再建函数(必须带
SQL SECURITY DEFINER):CREATE FUNCTION get_current_user_id() RETURNS INT READS SQL DATA DETERMINISTIC SQL SECURITY DEFINER BEGIN RETURN (SELECT business_user_id FROM auth_mapping WHERE conn_user = USER()); END; - 视图中调用它:
CREATE DEFINER='admin'@'localhost' SQL SECURITY DEFINER VIEW user_orders AS SELECT * FROM orders WHERE user_id = get_current_user_id(); - 函数内部可扩展:fallback 到
CURRENT_USER()、查缓存表、甚至调用 UDF 补充外部认证源
权限配置必须闭环,漏一环就等于没设防
哪怕视图定义再严,只要用户还能直连 orders 表,整套机制就形同虚设。
- 立刻收回底层表权限:
REVOKE SELECT ON mydb.orders FROM 'alice'@'%'; - 只授予视图权限:
GRANT SELECT ON mydb.user_orders TO 'alice'@'%'; - 验证是否生效:
SHOW GRANTS FOR 'alice'@'%';—— 输出里不能出现orders字样 - 如果视图涉及多张表(如
JOIN users和orders),DEFINER账户必须对每张基表都有对应权限,缺一张就会导致视图创建失败或运行时报错
最常被忽略的点是:存储函数必须用 SQL SECURITY DEFINER,且 DEFINER 账号需具备查询映射表的权限;同时,映射表本身也要严格控制写入权限——只有可信应用或 DBA 才能更新 auth_mapping,否则映射关系被篡改,行级隔离就崩了。


















