grant execute on procedure db_name.proc_name to 'u'@'h'是唯一有效方式;all privileges或dml权限无法替代,必须显式授权且带procedure关键字、全库名过程名,否则无效。

GRANT EXECUTE ON PROCEDURE 是唯一有效授权方式
用户对数据库有 ALL PRIVILEGES 或 SELECT, INSERT, UPDATE, DELETE 等权限,不代表能调用存储过程。MySQL 不把存储过程当作普通表对象来继承权限,EXECUTE 权限必须显式授予具体过程本身。
以下写法全部无效:
-
GRANT EXECUTE ON db_name.* TO 'u'@'h'—— 缺PROCEDURE关键字,MySQL 静默当成数据库级权限处理 -
GRANT SELECT ON db_name.* TO 'u'@'h'—— 表读权限 ≠ 过程执行权 -
GRANT CREATE ROUTINE ON db_name.* TO 'u'@'h'—— 只允许创建过程,不解决执行问题
正确写法只有一种:
GRANT EXECUTE ON PROCEDURE `db_name`.`proc_name` TO 'u'@'h';
注意:PROCEDURE 关键字不可省略,db_name.proc_name 必须写全名,不能模糊匹配或用通配符。
SHOW GRANTS 看不到 EXECUTE 就说明没授权成功
执行完 GRANT 后别急着重试,先验证是否生效:
SHOW GRANTS FOR 'u'@'h';
输出中必须出现类似这一行才表示成功:
GRANT EXECUTE ON PROCEDURE `db_name`.`proc_name` TO 'u'@'h'
如果只看到 USAGE、SELECT 或 EXECUTE ON db_name.*,说明授权失败或授错对象。
另外注意:'u'@'localhost' 和 'u'@'%' 是两个完全独立的用户,权限不互通。远程调用时检查 host 是否匹配。
EXECUTE 授权成功但 CALL 仍报错?查 SQL SECURITY DEFINER
即使 GRANT EXECUTE 成功,调用仍失败,大概率是存储过程定义时用了 SQL SECURITY DEFINER(MySQL 默认),导致权限校验不看调用者,而看创建者(DEFINER)是否存在、是否有底层表权限。
检查方法:
SHOW CREATE PROCEDURE proc_name;
确认输出中是否含 SQL SECURITY DEFINER。常见报错包括:
-
ERROR 1449 (HY000): The user specified as a definer ('xxx'@'%') does not exist——DEFINER用户已被删 -
ERROR 1142 (42000): SELECT command denied to user ... for table 't1'——DEFINER对过程内访问的某张表缺失权限
跨库操作时更明显:过程里有 SELECT * FROM other_db.t1,EXECUTE 权限只解决“能不能调”,不解决“过程内部能不能读 other_db.t1”——此时调用者还得额外有 other_db.t1 的 SELECT 权限。
修改 DEFINER 比改成 SQL SECURITY INVOKER 更稳妥
有人想简单改成 SQL SECURITY INVOKER,但这在生产环境风险高:
- 调用者必须拥有过程体中每条 SQL 的全部权限(比如
SELECT系统表mysql.proc、performance_schema,MySQL 8.0+ 还要SYSTEM_VARIABLES_ADMIN) - 过程里写了
SELECT * FROM mysql.user,而调用者没被授SELECT ON mysql.*,直接报错Access denied - 业务逻辑依赖固定权限上下文(如审计日志写入需高权限),
INVOKER会导致行为不一致
推荐做法:保持 SQL SECURITY DEFINER,统一把 DEFINER 改为一个已在目标库存在、且权限完备的专用账号(如 'proc_runner'@'%'),并严格授予其过程所需的最小权限集。
真正容易被忽略的是:DEFINER 修改后,过程内部若调用了其他函数或过程,那些对象的 DEFINER 也得同步检查并修复,否则重建后首次调用仍会失败。











