mysql 8.0.16+才支持grant execute on procedure语法,但须先授usage权限;默认sql security definer使过程以定义者权限运行,需显式设invoker才能校验调用者权限,且无细粒度view definition权限。

MySQL 不支持“只让某人调用某个存储过程,但不让看定义、不让改、不让删”这种精细到单对象的权限隔离——EXECUTE 权限能授,但 VIEW/ALTER/DELETE 必须靠其他权限组合控制,且默认不绑定调用者上下文。
GRANT EXECUTE ON PROCEDURE 语法是否有效取决于 MySQL 版本
MySQL 5.7 及更早版本完全不识别 GRANT EXECUTE ON PROCEDURE db.sp_name 语法,执行会报错 ERROR 1064;8.0.16+ 才真正稳定支持该写法,但有硬性前提:
- 必须先授予数据库级
USAGE权限:GRANT USAGE ON `db`.* TO 'u'@'%'(跳过这步,后续授权直接失败) - 不能用通配符代替过程名:
GRANT EXECUTE ON PROCEDURE db.*是非法语法,MySQL 不认 - 若用
GRANT EXECUTE ON db.*,授的是整个库下所有 routine(含函数、过程),不是仅过程 - 执行后无需
FLUSH PRIVILEGES(5.7.6+ 默认自动刷新,手动执行反而可能掩盖主机名匹配问题)
为什么给了 EXECUTE 权限,CALL 还报 “EXECUTE command denied”
这个错误常被误判为权限没给,实际多由 SQL SECURITY 模式和内部表权限共同触发:
- 过程默认是
SQL SECURITY DEFINER,内部 SQL 以定义者身份执行——但定义者账号(如'root'@'localhost')虽有权限,调用者却未必有对应表的SELECT权限,MySQL 统一抛EXECUTE command denied,不提示具体哪张表缺权限 - 查当前设置:
SELECT ROUTINE_NAME, SQL_SECURITY FROM INFORMATION_SCHEMA.ROUTINES WHERE ROUTINE_SCHEMA='db' AND ROUTINE_NAME='sp_name' - 验证定义者:
SHOW CREATE PROCEDURE db.sp_name,看DEFINER字段 - 若想让过程内所有操作都校验调用者权限,必须显式声明
SQL SECURITY INVOKER,创建时加,或用ALTER PROCEDURE db.sp_name SQL SECURITY INVOKER修改
如何防止用户绕过权限查看过程定义
EXECUTE 权限 ≠ 查看源码权限。SHOW CREATE PROCEDURE 需要 ALTER ROUTINE 权限,而它实际包含查看、修改、删除能力,生产环境慎授:
- 只允许调用?只给
EXECUTE即可 - 需要查定义?必须额外授
ALTER ROUTINE ON PROCEDURE db.sp_name TO 'u'@'%',但等同于开放修改权 - 没有更细粒度的 “VIEW DEFINITION” 权限;试图通过
SELECT查询INFORMATION_SCHEMA.ROUTINES只能看到 routine 名称、参数、创建时间等元信息,看不到完整CREATE语句 - MySQL 8.0+ 已禁用直接查
mysql.routines表,INFORMATION_SCHEMA.ROUTINES是唯一合法入口,且字段ROUTINE_DEFINITION值为NULL(出于安全限制)
调用失败的常见作用域陷阱
不是权限问题,而是库上下文错位导致的 PROCEDURE does not exist:
- 用户在
db1创建了sp_count,但连接后未USE db1,直接执行CALL sp_count()→ MySQL 在默认库(可能是空或mysql)里找,找不到 - 正确做法:始终带库名调用,
CALL db1.sp_count();或先USE db1再CALL sp_count() - 跨库调用时,过程内部若写
SELECT * FROM logs(无库前缀),实际查的是过程定义所在库,不是调用者当前库 ——USE不改变过程内 SQL 的默认库 - 动态库名只能靠预处理语句拼接:
SET @sql = CONCAT('CALL ', @db_name, '.sp_count()'); PREPARE stmt FROM @sql; EXECUTE stmt;,且调用者需对@db_name对应库有EXECUTE权限
最易被忽略的点:SQL SECURITY 设置决定权限检查边界,不改它,光授 EXECUTE 就像给门锁配钥匙却不换锁芯——表面能进,里面权限逻辑还是旧的。











