MySQL 8.0+ 中视图 DEFINER 权限仅在当前实例生效,需确保各实例均存在一致账号并显式授予 USAGE、SELECT、SHOW VIEW 等权限;PG 需用 SECURITY DEFINER 函数模拟,且函数拥有者须在每实例具备对应 schema 和表权限。
MySQL 8.0+ 中 CREATE VIEW 的 DEFINER 权限实际生效范围
视图的 definer 不是跨实例有效的,它只在当前实例内解析权限。哪怕两个实例用同一套账号体系(比如都同步了 mysql.user 表),definer='admin'@'%' 在实例 b 上执行时,会查实例 b 自己的 mysql.user 和 mysql.tables_priv,和实例 a 完全无关。
常见错误现象:SELECT 视图返回 ERROR 1356 (HY000): View 'db.v_user' references invalid table(s) or column(s) or function(s) or definer/invoker of view lack rights to use them,但表明明存在、用户也有 SELECT 权——问题出在 DEFINER 账号在当前实例中不存在或没对应库表权限。
- 必须确保每个目标实例中都创建了完全一致的
DEFINER账号,并显式授予其对视图所依赖对象的权限(不只是SELECT,还要包括SHOW VIEW) - 避免用
DEFINER=CURRENT_USER,它会让权限检查变成调用者视角,在跨实例场景下更难收敛 - 若用复制(如 GTID 复制),
DEFINER语句会被原样重放,但账号需提前在从库存在,否则复制中断
PostgreSQL 中 SECURITY DEFINER 函数包装视图的权限穿透逻辑
PG 没有“视图 DEFINER”概念,但可以用 SECURITY DEFINER 函数包裹 SELECT 逻辑来模拟。关键点在于:函数执行时以函数拥有者身份检查权限,而不是调用者;但这个拥有者必须在每个实例上真实存在且具备对应 schema/table 的 USAGE + SELECT 权限。
使用场景:需要让应用连接一个低权限账号,却能通过固定函数访问受限视图结果。
- 函数必须用
CREATE FUNCTION ... SECURITY DEFINER显式声明,且由特定账号(如view_admin)拥有 -
view_admin必须在每个目标实例中创建,并被授予USAGEon schema 和SELECTon underlying tables —— 仅授给视图本身无效 - 函数体里不能出现动态 SQL(如
EXECUTE拼接字符串),否则权限检查会回退到调用者,失去隔离意义 - 注意
search_path:函数内未显式指定 schema 的表名,会按函数创建时的search_path解析,跨实例时该路径可能不一致
跨实例权限同步时 mysql.db 和 information_schema 的陷阱
很多团队误以为只要同步了 mysql.user,权限就自动一致。其实 SELECT 权限可能落在 mysql.db(库级)、mysql.tables_priv(表级)甚至列级表中,而这些表不会被主从复制默认同步(除非开启 replicate_wild_ignore_table 例外)。
更隐蔽的问题:information_schema 是只读虚拟库,所有对其的权限检查最终映射到物理对象。例如对 information_schema.VIEWS 的查询权限,实际取决于你是否有权访问该视图定义中的源表 —— 这个判断发生在每个实例本地。
- 不要依赖
mysqldump mysql全量恢复权限,mysql.db等系统表在导入时可能被忽略或校验失败 - 推荐用
SHOW GRANTS FOR 'u'@'h'导出语句,再在各实例上重放;注意GRANT语句里的ON db.*要求db在目标实例上已存在 - 测试时别只查
SELECT * FROM v,要连带验证SHOW CREATE VIEW v—— 后者会暴露DEFINER是否可解析
代理层(如 ProxySQL、MaxScale)无法替代实例内权限隔离
有人试图在代理层做“视图路由”或“结果集过滤”,但这解决不了根本问题:视图元数据(CREATE VIEW 语句)、依赖关系、权限校验全在后端实例完成。代理看到的只是客户端发来的 SELECT 请求,它既不知道这个表是视图还是基表,也无法代替 MySQL 去检查 DEFINER 是否合法。
典型翻车点:ProxySQL 配置了 mysql_query_rules 把 SELECT FROM v_user 改写成 SELECT ... FROM t_user WHERE tenant_id=?,但应用仍可能直连实例绕过代理,或代理配置漏掉某条路径,导致权限逻辑分裂。
- 代理适合做负载均衡、读写分离、简单 SQL 改写,不适合承担细粒度对象级权限决策
- 如果必须用代理控制访问,应配合后端实例的
sql_mode=RESTRICTED_SESSION或只读账号,并关闭SHOW CREATE VIEW权限防止反推逻辑 - 真正需要跨实例统一权限模型的,得靠外部服务(如 Vault 动态生成临时账号)+ 实例预置权限模板,而不是指望代理“拦截并模拟”
最易被忽略的一点:视图嵌套。A 视图引用 B 视图,B 视图引用 C 表 —— 此时 A 的 DEFINER 必须同时满足对 B(作为视图)和 C(作为表)的权限,且这个检查链条在每个实例上独立发生。少一层授权,就会在某个实例上静默失败。

















