最近在作一個項目,因爲服務器切換,因此須要將原有服務器的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_%';