今天在一個修改過權限的MySQL數據庫遇到了“ERROR 1045 (28000): Access denied for user 'xxx'@'xxx.xxx.xxx.xxx' (using password: YES)”和“ERROR 1449 (HY000): The user specified as a definer ('xxx'@'xx') does not exist” 錯誤,花了點時間研究並重現該錯誤,並將其整理在此篇文章。
在測試數據庫MyDB,我們創建了一個mydbadmin的賬號,任意IP地址都可以訪問該數據庫,如下所示:
mysql> GRANT ALL PRIVILEGES ON `MyDB`.* TO 'mydbadmin'@'%' IDENTIFIED BY 'mydbadmin13s5';
Query OK, 0 rows affected (0.03 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
然后以這個mydbadmin登錄數據庫,創建一個視圖v_student. 當然,你也可以創建存儲過程或是函數等其他對象,它們也都會遇到這個錯誤。
mysql> desc student;
+----------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------+-------------+------+-----+---------+-------+
| stu_id | int(11) | YES | | NULL | |
| stu_name | varchar(12) | YES | | NULL | |
| sex | int(11) | YES | | NULL | |
| grade | int(11) | YES | | NULL | |
| age | int(11) | YES | | NULL | |
+----------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)
mysql> create or replace view v_student
-> as
-> select stu_name, sex, age
-> from student
-> where grade >=3;
Query OK, 0 rows affected (0.02 sec)
假如現在檢查時發現任意IP都可以訪問這個賬號是不符合安全規范的,然后刪除了這個賬號(如果你用rename user 也會遇到這個問題),重建了該賬號。此時你在較大權限的用戶下就會遇到“ERROR 1449 (HY000): The user specified as a definer ('mydbadmin'@'%') does not exist”錯誤。如下所示:
mysql> select user();
+----------------+
| user() |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)
mysql> select user,host from mysql.user where user='mydbadmin';
+-----------+------+
| user | host |
+-----------+------+
| mydbadmin | % |
+-----------+------+
1 row in set (0.00 sec)
mysql>
mysql> drop user mydbadmin@'%';
Query OK, 0 rows affected (0.02 sec)
mysql> select count(*) from v_student;
ERROR 1449 (HY000): The user specified as a definer ('mydbadmin'@'%') does not exist
mysql>
mysql> select user,host from mysql.user where user='mydbadmin';
Empty set (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON `MyDB`.* TO 'mydbadmin'@'192.168.%' IDENTIFIED BY 'mydbadmin135';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql>
然后你以在客戶端使用mydbadmin登錄后,查詢視圖就會報“ERROR 1045 (28000): Access denied for user 'mydbadmin'@'192.168.%' (using password: YES)”
mysql> select user();
+-------------------------+
| user() |
+-------------------------+
| mydbadmin@192.168.7.43 |
+-------------------------+
1 row in set (0.01 sec)
mysql> select * from v_student;
ERROR 1045 (28000): Access denied for user 'mydbadmin'@'192.168.%' (using password: YES)
原因分析
出現這個問題,是因為賬號mydbadmin@`%`已經不存在了。而視圖指定DEFINER為mydbadmin@`%`,此時創建者不存在了。而SQL SECURITY也是DEFINER,所以MySQL認為現在的用戶無權限訪問該視圖。所以有下面一些方法來解決這個錯誤。
解決方案
1:重建視圖(存儲過程或函數)即可解決問題。不過對於數據庫視圖很多的情況,這個方法略顯笨拙和繁瑣。
mysql> show create view v_student;
mysql> create or replace view v_student
-> as
-> select stu_name, sex, age
-> from student
-> where grade >=3;
Query OK, 0 rows affected (0.00 sec)
當然可以批量生成相關SQL,類似於下面SQL
SELECT CONCAT("alter DEFINER=`root`@`localhost` SQL SECURITY DEFINER VIEW ",TABLE_SCHEMA,".",TABLE_NAME," as ",VIEW_DEFINITION,";")
FROM information_schema.VIEWS
WHERE DEFINER=''mydbadmin@%';
2:如果創建視圖(存儲過程或函數)時,使用SQL SECURITY INVOKER,就可以避免出現這種情況。
如下所示,創建視圖時指定SQL SECURITY INVOKER.
SQL SECURITY { DEFINER | INVOKER } :指明誰有權限來執行。默認情況下,系統指定為DEFINE.
DEFINER 表示按定義者擁有的權限來執行
INVOKER 表示用調用者的權限來執行。
mysql> use MyDB;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select user();
+-------------------------+
| user() |
+-------------------------+
| mydbadmin@192.168.7.218 |
+-------------------------+
1 row in set (0.00 sec)
mysql> create or replace definer='mydbadmin'@'%'
-> sql security invoker
-> view v_student
-> as
-> select stu_name, sex, age
-> from student
-> where grade >=3;
Query OK, 0 rows affected (0.00 sec)
刪除用戶mydbadmin@'%',此時你會發現還能執行。
mysql> select user();
+----------------+
| user() |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)
mysql> drop user mydbadmin@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> select count(*) from v_student;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
mysql>
mysql> select user();
+----------------+
| user() |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)
mysql> GRANT ALL PRIVILEGES ON `MyDB`.* TO 'mydbadmin'@'192.168.%' IDENTIFIED BY 'mydbadmin135';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> select user();
+-------------------------+
| user() |
+-------------------------+
| mydbadmin@192.168.7.218 |
+-------------------------+
1 row in set (0.00 sec)
mysql> select count(*) from v_student;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
mysql>
3:這種方法只針對於存儲過程或函數 ,對於視圖而言,由於information_schema.VIEWS是無法修改的。所以無法使用此方法。如下測試所示:
mysql> UPDATE information_schema.VIEWS
-> SET DEFINER='root@localhost'
-> WHERE TABLE_NAME='v_student';
ERROR 1044 (42000): Access denied for user 'root'@'localhost' to database 'information_schema'
mysql> select user();
+-------------------------+
| user() |
+-------------------------+
| mydbadmin@192.168.7.34 |
+-------------------------+
1 row in set (0.00 sec)
mysql> use MyDB;
Database changed
mysql> DELIMITER &&
mysql> CREATE DEFINER='mydbadmin'@'%' PROCEDURE prc_my_test()
-> BEGIN
-> SELECT COUNT(*) FROM student;
-> END &&
Query OK, 0 rows affected (0.03 sec)
mysql> DELIMITER ;
mysql>
mysql> select user();
+----------------+
| user() |
+----------------+
| root@localhost |
+----------------+
1 row in set (0.00 sec)
mysql> select db, name, type ,security_type, definer
-> from mysql.proc
-> where name='prc_my_test';
+------+-------------+-----------+---------------+-------------+
| db | name | type | security_type | definer |
+------+-------------+-----------+---------------+-------------+
| MyDB | prc_my_test | PROCEDURE | DEFINER | mydbadmin@% |
+------+-------------+-----------+---------------+-------------+
1 row in set (0.00 sec)
mysql> call prc_my_test();
+----------+
| COUNT(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
mysql> drop user mydbadmin@'%';
Query OK, 0 rows affected (0.01 sec)
mysql> call prc_my_test();
ERROR 1449 (HY000): The user specified as a definer ('mydbadmin'@'%') does not exist
mysql> update mysql.proc set definer='root@localhost' where name='prc_my_test';
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select db, name, type ,security_type, definer
-> from mysql.proc
-> where name='prc_my_test';
+------+-------------+-----------+---------------+----------------+
| db | name | type | security_type | definer |
+------+-------------+-----------+---------------+----------------+
| MyDB | prc_my_test | PROCEDURE | DEFINER | root@localhost |
+------+-------------+-----------+---------------+----------------+
1 row in set (0.00 sec)
mysql> call prc_my_test();
ERROR 1449 (HY000): The user specified as a definer ('mydbadmin'@'%') does not exist
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> call prc_my_test();
ERROR 1449 (HY000): The user specified as a definer ('mydbadmin'@'%') does not exist
mysql> exit
Bye
如上所示,必須退出重新登錄,才能生效,如果更新完mysql.proc后,不退出當前會話,依然會報“ERROR 1449 (HY000): The user specified as a definer ('mydbadmin'@'%') does not exist”錯誤。
root@DB-Server ~]# mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 43
Server version: 5.6.20-enterprise-commercial-advanced-log MySQL Enterprise Server - Advanced Edition (Commercial)
Copyright (c) 2000, 2014, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> use MyDB;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> call prc_my_test();
+----------+
| COUNT(*) |
+----------+
| 0 |
+----------+
1 row in set (0.01 sec)
Query OK, 0 rows affected (0.01 sec)
這種方法,對於存儲過程或函數,意義在於可以批量修改,非常快捷方便。唯一比較遺憾的是對於VIEW,無法使用。
4: 重新創建賬號'mydbadmin'@'%' ,不過像這個案例,本身是處於安全考慮,限制能夠訪問數據庫的IP,那么此時這種方案就不太可行,如果只是誤刪用戶,那么這種方案就比較有效。
mysql> drop user 'mydbadmin'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> select count(*) from v_student;
ERROR 1449 (HY000): The user specified as a definer ('mydbadmin'@'%') does not exist
mysql> GRANT ALL PRIVILEGES ON `MyDB`.* TO 'mydbadmin'@'%' IDENTIFIED BY 'mydbadmin13s5';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> select count(*) from v_student;
+----------+
| count(*) |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
mysql>
另外,對於存儲過程、函數、定時事件、視圖,都可以參考上面方法進行,其中定時事件主要修改mysql.event下的表。
update mysql.event set definer='root@localhost';
參考資料:
http://kedar.nitty-witty.com/blog/solutions-mysql-error-1449-the-user-specified-as-a-definer-does-not-exist