前言: mysql
咱們在建立數據庫用戶的時候都會指定host,即一個完整的用戶可描述爲 'username'@'host' 。建立用戶時不顯式指定host則默認爲%,%表明全部ip段均可以使用這個用戶,咱們也能夠指定host爲某個ip或ip段,這樣會僅容許在指定的ip主機使用該數據庫用戶。不過你也應該明白 'username'@'%' 和 'username'@'192.168.6.%' 是兩個毫無關聯的用戶,這兩個用戶能夠有不一樣的密碼和權限,這裏不建議建立多個同名不一樣host的用戶,還有不要輕易更改用戶的host,筆者曾經遇到過由於更改用戶host引起的故障,下面將其分享出來,爲你講述來龍去脈。sql
當時爲了規範安全,將某個程序用戶的host由%改成了應用服務器ip段,過段時間業務反饋某些功能報錯,經排查發現是由於沒法調用存儲過程(你們能夠先思考下緣由),下面模擬下故障操做。數據庫
# 原有用戶、表、存儲過程模擬建立 mysql> create user 'testuser'@'%' identified by '123456'; Query OK, 0 rows affected (0.04 sec) mysql> grant select,insert,update,delete,execute on `testdb`.* to 'testuser'@'%'; Query OK, 0 rows affected (0.01 sec) mysql> flush privileges; Query OK, 0 rows affected (0.00 sec) mysql> show grants for 'testuser'@'%'; +-------------------------------------------------------------------------------+ | Grants for testuser@% | +-------------------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'testuser'@'%' | | GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON `testdb`.* TO 'testuser'@'%' | +-------------------------------------------------------------------------------+ CREATE TABLE `students` ( `id` int(11) NOT NULL , `name` varchar(20), `age` int(11), PRIMARY KEY (`id`) ) ENGINE=InnoDB ; INSERT INTO `students` VALUES ('1001', 'lodd', '23'); INSERT INTO `students` VALUES ('1002', 'sdfs', '21'); INSERT INTO `students` VALUES ('1003', 'sdfsa', '24'); DROP PROCEDURE IF EXISTS select_students_count; DELIMITER $$ CREATE DEFINER=`testuser`@`%` PROCEDURE `select_students_count`() BEGIN SELECT count(id) from students; END $$ DELIMITER ; # 使用testuser用戶調用存儲過程 調用正常 mysql> call select_students_count(); +-----------+ | count(id) | +-----------+ | 3 | +-----------+ # 更改用戶host 重命名用戶 mysql> RENAME USER 'testuser'@'%' to 'testuser'@'192.168.6.%'; Query OK, 0 rows affected (0.00 sec) mysql> flush privileges; Query OK, 0 rows affected (0.01 sec) mysql> show grants for 'testuser'@'192.168.6.%'; +---------------------------------------------------------------------------------------+ | Grants for testuser@localhost | +---------------------------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'testuser'@'localhost' | | GRANT SELECT, INSERT, UPDATE, DELETE, EXECUTE ON `testdb`.* TO 'testuser'@'localhost' | +---------------------------------------------------------------------------------------+ # 再次用testuser用戶調用存儲過程 沒法調用 出現故障 mysql> call select_students_count(); ERROR 1449 (HY000): The user specified as a definer ('testuser'@'%') does not exist
其實咱們手動調用下存儲過程後,從報錯內容明顯能夠看出是由於'testuser@'%'用戶不存在的問題。由於該存儲過程的定義者是'testuser@'%',而咱們將此用戶的host改爲了192.168.6.%,那麼當咱們以後調用該存儲過程時,系統判別到此存儲過程的屬主用戶不存在,所以系統拒絕請求並拋出異常。segmentfault
當知道上述緣由後,解決方法就會明朗許多,咱們只須要將該存儲過程的屬主改成新的用戶便可。其實更改過用戶後,該用戶下的視圖、存儲過程、函數、觸發器、事件都會受到影響,當咱們定義視圖、存儲過程、函數時使用 DEFINER
屬性時,若調用這些對象,系統會首先判別此對象的屬主用戶是否存在,不存在會直接拋出錯誤。安全
此問題的解決方案有兩種,一是將此存儲過程的安全屬性由 DEFINER
改成 INVOKER
,我的不推薦這個方案,至於 DEFINER
和 INVOKER
的區別,下個章節會額外講解。二是更改此存儲過程的屬主,下面給出更改方法並加以驗證:服務器
# 經過系統表更改存儲過程的屬主 mysql> update mysql.proc set definer='testuser@192.168.6.%' where db='testdb' and name='select_students_count' and type='PROCEDURE'; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 # 使用testuser用戶調用驗證 調用成功 mysql> call select_students_count(); +-----------+ | count(id) | +-----------+ | 3 | +-----------+ 1 row in set (0.00 sec)
MySQL中,建立視圖(view)、函數(function)、存儲過程(procedure)、觸發器(trigger)、事件(event)時,能夠指定安全驗證方式(也就是SQL SECURITY)屬性,其值能夠爲DEFINER或INVOKER,表示在執行過程當中,使用誰的權限來執行。運維
默認狀況下,系統指定爲DEFINER。當SQL SECURITY屬性爲DEFINER時,數據庫中必須存在DEFINER指定的用戶,而且該用戶擁有對應的操做權限及引用的相關對象的權限,才能成功執行。與當前用戶是否有權限無關。當SQL SECURITY屬性爲INVOKER時,只要執行者有執行權限而且有引用的相關對象的權限,就能夠成功執行。ide
瞭解了上述知識後,可能你早已明白上述故障發生的來龍去脈。在平常生產中,不建議使用INVOKER屬性,由於將SQL SECURITY定義爲INVOKER後,其餘用戶想調用此對象時不只須要有該對象的執行權限還要有其餘引用到的相關對象的權限,極大的增長了運維複雜性。下面回顧整篇文章,整理出一下幾點我的建議,以供你們參考:函數
總結: spa
本文從一個故障出發,詳細記錄了故障發生的緣由及背後涉及的知識,其實像DEFINER屬性這些細節類的東西很容易被忽視,只有遇到問題了咱們纔會去探究。但願本篇文章能讓你學到新東西,特別是上面總結的幾點建議都是筆者平常運維總結出的。原創不易,請你們多多支持!