記一次MySQL數據庫拒絕訪問的解決過程


問題背景

用wordpress搭博客,數據庫采用MySQL。為了調試方便,創建賬戶my_account ,允許它從任意主機訪問數據庫。

CREATE USER `my_account`@'%' IDENTIFIED BY 'my_password';

修改 wp-config.php 相應配置,注意 DB_HOST 設置為 127.0.0.1

define('DB_USER', 'my_account'); // 賬號
define('DB_PASSWORD', 'my_password'); // 密碼
define('DB_HOST', '127.0.0.1'); // 數據服務地址

部署到雲服務器上,本地瀏覽器訪問博客,提示數據庫拒絕訪問(本地連接遠程數據沒問題),以下為錯誤日志。

ERROR 1045 (28000): Access denied for user 'my_account'@'localhost' (using password: YES)`

簡單排查后,解決了問題,這里記錄下解決方案,以及出錯的原因。

解決方案

1、方案一:刪除 mysql.user表 中,Host字段為 localhost 的匿名賬號(賬戶名為空)。
2、方案二:創建 my_account@localhost 賬戶,用於本地連接數據庫。

筆者采用了方案 一。

首先,確認下 mysql.user 表中是否存在匿名賬戶。

MariaDB [(none)]> SELECT User, Host from mysql.user WHERE Host = 'localhost' AND User = '';
+------+-----------+
| User | Host      |
+------+-----------+
|      | localhost |
+------+-----------+
1 row in set (0.00 sec)

接着,刪除相應匿名賬戶,再次嘗試登陸,成功。

MariaDB [(none)]> DROP USER ''@'localhost';
Query OK, 0 rows affected (0.00 sec)

問題分析

為什么匿名賬戶會導致數據庫連接失敗?

需要對MySQL的賬戶創建、客戶端連接校驗有一定的了解。

創建MySQL賬戶

基礎語法如下:

CREATE USER 賬戶名@主機 IDENTIFIED BY 密碼;

注意點:(以下用 User 指代賬戶名,Host 指代 主機)

  1. Host 表示 允許賬戶從哪台主機訪問數據庫。主要用於做安全限制,可以是 主機名、IP地址、%(通配符);
  2. User 允許重復,只要 Host 是不同的就行。
  3. 當 Host 設置為 % 時,表示允許從任意主機連接數據庫。

比如,存在兩個xiaoming賬戶,一個允許從本機連接數據庫,一個允許從 14.215.177.39 連接數據庫。

MariaDB [(none)]> SELECT User, Host FROM mysql.user WHERE User = 'xiaoming';           
+---------+---------------+
| User    | Host          |
+---------+---------------+
| xiaoming | 14.215.177.39 |
| xiaoming | localhost     |
+---------+---------------+
2 rows in set (0.00 sec)

匿名賬戶

也就是 User 為空的賬戶,可以匹配任意用戶名。如下指令創建了匿名賬戶。

CREATE USER ''@'localhost' IDENTIFIED BY 'pwd3';

身份校驗

數據庫服務器收到客戶端連接,首先會進行身份校驗,將 User、Host、Password 字段,跟 mysql.user 表里的記錄進行比較,確認是否合法賬戶。

這里有個問題:如果 mysql.user表 中存在多條匹配記錄,該以哪條記錄為准?

答案是“優先級”。大致規則如下:

  1. 首先,檢查 Host 字段。如果有多個 Host 符合條件,則選擇匹配度最高的記錄(IP地址 > 通配符%)。
  2. 其次,檢查 User 字段。如果有多個 User 符合條件,則選擇匹配度最高的記錄。匿名用戶可以匹配任何用戶,因此匹配度最低。

優先級匹配例子

舉例,假設本地數據庫有如下兩個賬戶(Password字段實際非明文)。

+------------+-----------+-----------+
| User       | Host      | Password  |
+------------+-----------+-----------+
| my_account | %         | 123       |
|            | localhost | 456       |
+------------+-----------+-----------+

運行如下命令,最終登錄的賬戶,匹配的是 第2條 記錄。(讀者可自行嘗試,輸入密碼123登錄失敗,輸入456登錄成功)

mysql -u my_account -p

為什么呢?回顧下匹配的優先級。

  1. 首先,檢查 Host 字段。localhost、% 都符合要求。localhost 匹配度高於 %,因此匹配到第2條記錄。
  2. 接着,檢查 User 字段。第2條記錄是匿名賬戶,可匹配任意User值,因此,第2條記錄符合要求。

因此,雖然賬戶 my_account 的 Host字段 為%,但是在本地(數據庫所在主機)連接數據庫時,因為上述規則的存在,MySQL 會認為你是用匿名賬戶在登錄。

my_account 跟匿名賬戶的密碼是不同的,因此密碼校驗不通過,拒絕訪問。

寫在后面

因匿名用戶存在,導致本地連接數據庫拒絕連接,這個問題很常見,有不少人認為這是MySQL的設計缺陷,比如 這里

了解了MySQL的身份驗證邏輯,遇到類似問題也就有思路了。關於%通配符,匹配優先級,上文並沒有詳細展開,感興趣的讀者可以自行查看官方文檔。

另外,MySQL拒絕訪問的原因有多種,讀者應具體問題具體分析。

如有錯漏,敬請指出。

相關鏈接

Host wildcard % which is said in docs that means "all hosts" excludes localhost

6.2.4 Access Control, Stage 1: Connection Verification

原文首發在筆者的個人博客,地址在這里


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM