最近在作一个项目,因为服务器切换,因此须要将原有服务器的mysql数据表以及存储过程导入到另外一个服务器的mysql数据库中。导入完成以后觉得一切是那么的简单,却没有想到总仍是出现了一些莫名其妙的问题。mysql
我在用程序调用存储过程时,老是提示错误:sql
1 The user specified as a definer ('test'@'%') does not exist 1449
查看了本身mysql的用户表后,发现确实没有test这个用户,可是我程序用的是root登陆的,因此感受有些莫名其妙。数据库
后来经过查资料发现,是因为本身存储过程设置的安全性为definer,而当时的那个数据库存在test这个用户且用的test用户建立的存储过程。安全
因此解决方法主要有如下两种:服务器
保持definer安全性函数
1)在navicat上进行修改this
将定义者从test改成在该服务器存在的用户(通常每一个服务器都有root@localhost)spa
2)经过sql语句修改code
1 mysql>update mysql.proc set DEFINER='root@localhost' WHERE NAME='' AND db='mydb';
其中,mysql.proc是固定的,definer即要改成的用户名,name为存储过程名,db为数据库名对象
将安全性修改成invoker
1)在navicat上进行修改
2)经过sql语句进行修改
1 ALTER PROCEDURE proc_name SQL SECURITY INVOKER 2 ALTER PROCEDURE proc_name SQL SECURITY DEFINER
引伸阅读:mysql存储过程的definer和invoker
【用户操做存储过程的权限】
1 ALTER ROUTINE -- 编辑或删除存储过程 2 3 CREATE ROUTINE -- 建立存储过程 4 5 EXECUTE -- 运行存储过程
【存储过程的建立语法】
1 delimiter // -- 声明分隔符(命令结束符) 2 3 create 4 5 definer = user@hostname | current_user 6 7 procedure 存储过程名 (参数) 8 9 comment '注释' 10 11 sql security definer | invoker -- sql 的安全设置 12 13 begin 14 15 存储过程的body 16 17 end 18 19 // 20 21 delimiter ; -- 声明分隔符(命令结束符)
【函数的建立语句】
1 delimiter // -- 声明分隔符(命令结束符) 2 3 create 4 5 definer = user@hostname | current_user 6 7 function 函数名(参数) 8 9 return 返回值类型 10 11 comment '注释' 12 13 sql security definer | invoker -- sql 的安全设置 14 15 begin 16 17 函数的body 18 19 end 20 // 21 22 delimiter ; -- 声明分隔符(命令结束符)
【definer和invoker的解释】
建立存储过程的时候能够指定 SQL SECURITY属性,设置为 DEFINER 或者INVOKER,用来告诉mysql在执行存储过程的时候,是以DEFINER用户的权限来执行,仍是以调用者的权限来执行。
默认状况下,使用DEFINER方式,此时调用存储过程的用户必须有存储过程的EXECUTE权限,而且DEFINER指定的用户必须是在mysql.user表中存在的用户。
DEFINER模式下,默认DEFINER=CURRENT_USER,在存储过程执行时,mysql会检查DEFINER定义的用户'user_name'@'host_name'的权限;
INVOKER模式下,在存储过程执行时,会检查存储过程调用者的权限。
若是SQL SECURITY子句指定为DEFINER,存储过程将使用存储过程的DEFINER执行存储过程,验证调用存储过程的用户是否具备存储过程的execute权限和DEFINER用户是否具备存储过程引用的相关对象的权限;
若是SQL SECURITY子句指定为INVOKER,那么MySQL将使用当前调用存储过程的用户执行此过程,并验证用户是否具备存储过程的execute权限和存储过程引用的相关对象的权限;
案例一:DEFINER
1 CREATE DEFINER = 'admin'@'localhost' PROCEDURE account_count() 2 BEGIN 3 SELECT 'Number of accounts:', COUNT(*) FROM mysql.user; 4 END;
在这个案例中,不论哪一个用户A调用存储过程,存储过程都会以'admin'@'localhost'的权限去执行,即便这个用户A没有查询mysql.user表的权限。
案例二:INVOKER
1 CREATE DEFINER = 'admin'@'localhost' PROCEDURE account_count() 2 SQL SECURITY INVOKER 3 BEGIN 4 SELECT 'Number of accounts:', COUNT(*) FROM mysql.user; 5 END;
在这个案例中,虽然存储过程语句中仍然带有DEFINER参数,可是因为SQL SECURITY指定了INVOKER,因此在存储过程执行的时候,会以调用者的额身份去执行。此时这个存储过程是否能成功执行,取决于调用者是否有mysql.user表的查询权限。
【案例】
案例一:调用存储过程
存储过程的调用者是 : admin@192.168.1.1
存储过程的DEFINER是 : admin@%
MySQL中存在的用户是 : admin@192.168.%.%
此时admin@192.168.1.1是能够访问数据库的,由于它符合admin@192.168.%.%的受权规则,可是当它调用DEFINER='admin@%'的存储过程的时候,mysql会检查mysql.user用户表中是否存在admin@%这个用户,mysql的检查结果是admin@%这个用户不存在,此时就会返回报错,提示“Ther user specified as a definer ('admin@%') does not exist.。
案例二:建立存储过程
使用用户admin@192.168.1.1链接mysql,该用户有test库的all privileges,执行建立存储过程的操做:
存储过程当中定义的DEFINER是 : admin@%
MySQL中存在的用户是 : admin@192.168.%.%
此时,会遇到报错,提示”ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER privilege(s) for this operation“
修复DEFINER='admin@192.168.%.%',或者去掉 DEFINER参数,均可以恢复正常。
说明:
案例一中是存在问题的,存储过程的调用者和拥有者都是admin@192.168.1.1,可是DEFINER倒是admin@%,这是因为建立存储过程的命令是由root用户执行的,因此没有遇到案例二中的报错。
【存储过程经常使用命令】
1 -- 查看存储过程的建立语句: 2 3 show create procedure 存储过程名; 4 5 -- 查看存储过程的信息: 6 7 show procedure status like '存储过程名'G 8 9 -- 查看存储过程的Definer信息: 10 11 select db,name,type,sql_security,definer from mysql.proc where type='PROCEDURE' and db='数据库名' ; 12 13 -- 修改存储过程的DEFINER: 14 15 update mysql.proc set `definer` ='admin@192.168.%.%' where db like 'db_%';