當您嘗試連線至 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
表格列具有空白的使用者名稱,則該使用者會被視為沒有名稱的匿名使用者,而不是具有用戶端實際指定名稱的使用者。這表示在連線期間(即在階段 2 期間),空白使用者名稱將用於所有進一步的存取檢查。
authentication_string
資料行可以為空白。這不是萬用字元,也不表示任何密碼都符合。它表示使用者必須在不指定密碼的情況下連線。驗證用戶端的插件所實作的驗證方法可能會使用或可能不會使用 authentication_string
資料行中的密碼。在這種情況下,也有可能使用外部密碼來驗證到 MySQL 伺服器。
儲存在 user
資料表的 authentication_string
資料行中的非空白密碼值會被加密。MySQL 不會將密碼以明文方式儲存供任何人查看。相反地,嘗試連線的使用者提供的密碼會被加密(使用帳戶驗證插件所實作的密碼雜湊方法)。然後,加密後的密碼會在連線過程中用於檢查密碼是否正確。這是在沒有加密密碼透過連線傳輸的情況下完成的。請參閱第 8.2.1 節「帳戶使用者名稱和密碼」。
從 MySQL 伺服器的角度來看,加密後的密碼是真正的密碼,因此您絕對不應該讓任何人存取它。特別是,請勿讓非管理使用者讀取 mysql
系統資料庫中的表格。
下表顯示 user
資料表中 User
和 Host
值的各種組合如何應用於傳入的連線。
User 值 |
Host 值 |
允許的連線 |
---|---|---|
'fred' |
'h1.example.net' |
fred ,從 h1.example.net 連線 |
'' |
'h1.example.net' |
任何使用者,從 h1.example.net 連線 |
'fred' |
'%' |
fred ,從任何主機連線 |
'' |
'%' |
任何使用者,從任何主機連線 |
'fred' |
'%.example.net' |
fred ,從 example.net 網域中的任何主機連線 |
'fred' |
'x.example.%' |
fred ,從 x.example.net 、x.example.com 、x.example.edu 等等連線;這可能沒有用處 |
'fred' |
'198.51.100.177' |
fred ,從 IP 位址為 198.51.100.177 的主機連線 |
'fred' |
'198.51.100.%' |
fred ,從 198.51.100 C 類子網路中的任何主機連線 |
'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 | ...
+-----------+----------+-
當用戶端嘗試連線時,伺服器會搜尋排序後的資料列,並使用找到的第一個匹配項。對於 jeffrey
從 localhost
的連線,資料表中兩個資料列符合:一個是 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
的連線首先不是由 User
資料行值包含 'jeffrey'
的資料列匹配,而是由沒有使用者名稱的資料列匹配。因此,jeffrey
會被驗證為匿名使用者,即使他在連線時指定了使用者名稱。
如果您可以連線到伺服器,但您的權限與您預期的不符,您可能正在被驗證為其他帳戶。若要找出伺服器用來驗證您的帳戶,請使用 CURRENT_USER()
函數。(請參閱第 14.15 節「資訊函數」。)它會以
格式傳回一個值,該值指示匹配的 user_name
@host_name
user
資料表資料列中的 User
和 Host
值。假設 jeffrey
連線並發出以下查詢
mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost |
+----------------+
此處顯示的結果表示匹配的 user
資料表資料列具有空白的 User
資料行值。換句話說,伺服器將 jeffrey
視為匿名使用者。
診斷驗證問題的另一種方法是列印出 user
資料表並手動排序,以查看第一個匹配項是在哪裡建立的。