當您嘗試連線到 MySQL 伺服器時,伺服器會根據下列條件接受或拒絕連線
您的身分以及您是否可以透過提供適當的憑證來驗證它。
您的帳戶是否已鎖定或解除鎖定。
伺服器會先檢查憑證,然後檢查帳戶鎖定狀態。在任何一個步驟發生錯誤都會導致伺服器完全拒絕您的存取。否則,伺服器會接受連線,然後進入階段 2 並等待請求。
伺服器會使用 user
表中的欄位執行身分和憑證檢查,只有在滿足以下條件時才會接受連線
用戶端主機名稱和使用者名稱與某些
user
表列中的Host
和User
欄位相符。有關允許的Host
和User
值的規則,請參閱 第 8.2.4 節,「指定帳戶名稱」。用戶端會提供該列中指定的憑證(例如,密碼),如
authentication_string
欄位所示。憑證會使用plugin
欄位中命名的驗證外掛程式進行解釋。該列表示帳戶已解除鎖定。鎖定狀態記錄在
account_locked
欄位中,其值必須為'N'
。可以使用CREATE USER
或ALTER USER
陳述式設定或變更帳戶鎖定。
您的身分基於兩個資訊
您的 MySQL 使用者名稱。
您連線的用戶端主機。
如果 User
欄位值為非空白,則傳入連線的使用者名稱必須完全符合。如果 User
值為空白,則符合任何使用者名稱。如果符合傳入連線的 user
資料表列的使用者名稱為空白,則該使用者會被視為沒有名稱的匿名使用者,而不是具有用戶端實際指定名稱的使用者。這表示在連線期間(即在第二階段)的所有進一步存取檢查都將使用空白的使用者名稱。
authentication_string
欄位可以為空白。這不是萬用字元,並不表示符合任何密碼。它表示使用者必須在不指定密碼的情況下連線。驗證用戶端的外掛程式實作的驗證方法可能會也可能不會使用 authentication_string
欄位中的密碼。在這種情況下,也可能使用外部密碼來驗證 MySQL 伺服器。
儲存在 user
資料表的 authentication_string
欄位中的非空白密碼值是加密的。MySQL 不會將密碼儲存為明文,讓任何人都能看到。相反地,嘗試連線的使用者所提供的密碼會被加密(使用帳戶驗證外掛程式實作的密碼雜湊方法)。然後,在連線過程中,會使用加密後的密碼來檢查密碼是否正確。這是在加密後的密碼永遠不會在連線中傳輸的情況下完成的。請參閱第 8.2.1 節,「帳戶使用者名稱和密碼」。
從 MySQL 伺服器的角度來看,加密後的密碼是真正的密碼,因此您絕對不應該讓任何人存取它。特別是,不要讓非管理使用者讀取 mysql
系統資料庫中的資料表。
下表顯示 user
資料表中 User
和 Host
值的各種組合如何應用於傳入連線。
User 值 |
Host 值 |
允許的連線 |
---|---|---|
'fred' |
'h1.example.net' |
從 h1.example.net 連線的 fred |
'' |
'h1.example.net' |
從 h1.example.net 連線的任何使用者 |
'fred' |
'%' |
從任何主機連線的 fred |
'' |
'%' |
從任何主機連線的任何使用者 |
'fred' |
'%.example.net' |
從 example.net 網域中的任何主機連線的 fred |
'fred' |
'x.example.%' |
從 x.example.net 、x.example.com 、x.example.edu 等連線的 fred ;這可能沒有用 |
'fred' |
'198.51.100.177' |
從具有 IP 位址 198.51.100.177 的主機連線的 fred |
'fred' |
'198.51.100.%' |
從 198.51.100 C 類子網路中的任何主機連線的 fred |
'fred' |
'198.51.100.0/255.255.255.0' |
與上一個範例相同 |
傳入連線的用戶端主機名稱和使用者名稱有可能與 user
資料表中的多列相符。上述範例集展示了這一點:顯示的幾個條目符合來自 h1.example.net
的 fred
的連線。
當有多個符合項時,伺服器必須決定要使用哪一個。它會按照以下方式解決此問題
每當伺服器將
user
資料表讀取到記憶體時,它會對這些列進行排序。當用戶端嘗試連線時,伺服器會依排序順序搜尋這些列。
伺服器會使用第一個符合用戶端主機名稱和使用者名稱的列。
伺服器會使用排序規則,先排序具有最特定 Host
值的列
實體的 IP 位址和主機名稱是最特定的。
主機部分具有 IP 位址的帳戶具有以下特定順序
主機部分以 IP 位址給出的帳戶
CREATE USER 'user_name'@'127.0.0.1'; CREATE USER 'user_name'@'198.51.100.44';
主機部分以使用 CIDR 表示法的 IP 位址給出的帳戶
CREATE USER 'user_name'@'192.0.2.21/8'; CREATE USER 'user_name'@'198.51.100.44/16';
主機部分以使用子網路遮罩的 IP 位址給出的帳戶
CREATE USER 'user_name'@'192.0.2.0/255.255.255.0'; CREATE USER 'user_name'@'198.51.0.0/255.255.0.0';
模式
'%'
表示「任何主機」,且最不特定。空字串
''
也表示「任何主機」,但在'%'
之後排序。
非 TCP(socket 檔案、具名管道和共用記憶體)連線會被視為本機連線,如果存在任何此類帳戶,則會比對 localhost
的主機部分,否則會比對符合 localhost
的萬用字元主機部分(例如,local%
、l%
、%
)。
將 '%'
視為等同於 localhost
的處理方式已過時;您應該預期此行為將從未來版本的 MySQL 中移除。
具有相同 Host
值的列會先排序具有最特定 User
值的列。空白的 User
值表示「任何使用者」,且最不特定,因此對於具有相同 Host
值的列,非匿名使用者會在匿名使用者之前排序。
對於具有同等特定 Host
和 User
值的列,順序是不確定的。
若要瞭解其運作方式,假設 user
資料表如下所示
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| % | root | ...
| % | jeffrey | ...
| localhost | root | ...
| localhost | | ...
+-----------+----------+-
當伺服器將資料表讀取到記憶體時,它會使用剛描述的規則對這些列進行排序。排序後的結果如下
+-----------+----------+-
| Host | User | ...
+-----------+----------+-
| localhost | root | ...
| localhost | | ...
| % | jeffrey | ...
| % | root | ...
+-----------+----------+-
當用戶端嘗試連線時,伺服器會搜尋排序後的列,並使用找到的第一個符合項。對於來自 localhost
的 jeffrey
連線,資料表中的兩列符合:Host
和 User
值為 'localhost'
和 ''
的列,以及值為 '%'
和 'jeffrey'
的列。 'localhost'
列在排序順序中首先出現,因此這是伺服器使用的列。
這是另一個範例。假設 user
資料表如下所示
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| % | jeffrey | ...
| h1.example.net | | ...
+----------------+----------+-
排序後的資料表如下所示
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| h1.example.net | | ...
| % | jeffrey | ...
+----------------+----------+-
第一列符合來自 h1.example.net
的任何使用者的連線,而第二列符合來自任何主機的 jeffrey
的連線。
一種常見的誤解是認為,對於給定的使用者名稱,當伺服器嘗試尋找連線的符合項時,會先使用明確命名該使用者的所有列。這是不正確的。前面的範例說明了這一點,其中來自 h1.example.net
的 jeffrey
連線首先比對的是沒有使用者名稱的列,而不是將 'jeffrey'
作為 User
欄位值的列。因此,jeffrey
會被驗證為匿名使用者,即使他在連線時指定了使用者名稱。
如果您能夠連線到伺服器,但您的權限與您預期的不符,則您可能正在被驗證為其他帳戶。若要找出伺服器用於驗證您的帳戶,請使用 CURRENT_USER()
函數。(請參閱第 14.15 節,「資訊函數」。)它會以
格式傳回一個值,指出符合的 user_name
@host_name
user
資料表列中的 User
和 Host
值。假設 jeffrey
連線並發出以下查詢
mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost |
+----------------+
這裡顯示的結果表示符合的 user
資料表列具有空白的 User
欄位值。換句話說,伺服器將 jeffrey
視為匿名使用者。
診斷驗證問題的另一種方法是列印出 user
資料表,並手動排序它,以查看首先進行符合項的位置。