可插拔身份驗證實現的功能之一是代理使用者(請參閱 代理使用者)。若要讓伺服器端身份驗證插件參與代理使用者支援,必須滿足以下條件
當連線的客戶端應被視為代理使用者時,插件必須在
MYSQL_SERVER_AUTH_INFO
結構的authenticated_as
成員中傳回不同的名稱,以指示被代理的使用者名稱。它也可以選擇性地設定external_user
成員,以設定external_user
系統變數的值。必須設定代理使用者帳戶,以便透過插件進行身份驗證。使用
CREATE USER
或GRANT
陳述式,將帳戶與插件相關聯。
換句話說,插件所需的代理使用者支援的唯一方面是將 authenticated_as
設定為被代理的使用者名稱。其餘是可選的(設定 external_user
),或由 DBA 使用 SQL 陳述式完成。
當代理使用者連線時,身份驗證插件如何決定要傳回哪個被代理的使用者?這取決於插件。通常,插件會根據伺服器傳遞給它的身份驗證字串,將客戶端對應到被代理的使用者。此字串來自指定使用插件進行身份驗證的 CREATE USER
陳述式的 IDENTIFIED WITH
子句的 AS
部分。
插件開發人員會決定身份驗證字串的語法規則,並根據這些規則實作插件。假設插件接受以逗號分隔的配對清單,將外部使用者對應到 MySQL 使用者。例如
CREATE USER ''@'%.example.com'
IDENTIFIED WITH my_plugin AS 'extuser1=mysqlusera, extuser2=mysqluserb'
CREATE USER ''@'%.example.org'
IDENTIFIED WITH my_plugin AS 'extuser1=mysqluserc, extuser2=mysqluserd'
當伺服器叫用插件來驗證客戶端時,它會將適當的身份驗證字串傳遞給插件。插件負責
將字串解析為其組成部分,以確定要使用的對應
將客戶端使用者名稱與對應進行比較
傳回正確的 MySQL 使用者名稱
例如,如果 extuser2
從 example.com
主機連線,伺服器會將 'extuser1=mysqlusera, extuser2=mysqluserb'
傳遞給插件,並且插件應將 mysqluserb
複製到 authenticated_as
中,並加上終止的空位元組。如果 extuser2
從 example.org
主機連線,伺服器會傳遞 'extuser1=mysqluserc, extuser2=mysqluserd'
,並且插件應改為複製 mysqluserd
。
如果在對應中沒有相符項,則動作取決於插件。如果需要相符項,插件很可能會傳回錯誤。或者插件可能只會傳回客戶端名稱;在這種情況下,它不應變更 authenticated_as
,伺服器也不會將客戶端視為代理。
以下範例示範如何使用名為 auth_simple_proxy
的插件來處理代理使用者。如同先前所述的 auth_simple
插件一樣,auth_simple_proxy
接受任何非空的密碼為有效密碼(因此不應在生產環境中使用)。此外,它會檢查 auth_string
身份驗證字串成員,並使用這些非常簡單的規則來解讀它
如果字串為空,插件會按給定的方式傳回使用者名稱,且不會發生代理。也就是說,插件不會變更
authenticated_as
的值。如果字串不為空,插件會將其視為被代理的使用者名稱,並將其複製到
authenticated_as
,以便發生代理。
為了進行測試,請根據前面的規則設定一個未被代理的帳戶,以及一個被代理的帳戶。這表示一個帳戶沒有 AS
子句,而另一個帳戶則包含一個命名被代理使用者的 AS
子句
CREATE USER 'plugin_user1'@'localhost'
IDENTIFIED WITH auth_simple_proxy;
CREATE USER 'plugin_user2'@'localhost'
IDENTIFIED WITH auth_simple_proxy AS 'proxied_user';
此外,為被代理的使用者建立一個帳戶,並授予 plugin_user2
對它的 PROXY
權限
CREATE USER 'proxied_user'@'localhost'
IDENTIFIED BY 'proxied_user_pass';
GRANT PROXY
ON 'proxied_user'@'localhost'
TO 'plugin_user2'@'localhost';
在伺服器叫用身份驗證插件之前,它會將 authenticated_as
設定為客戶端使用者名稱。若要指示使用者是代理,插件應將 authenticated_as
設定為被代理的使用者名稱。對於 auth_simple_proxy
,這表示它必須檢查 auth_string
值,並且如果值不為空,則將其複製到 authenticated_as
成員,以將其傳回為被代理的使用者名稱。此外,當發生代理時,插件會將 external_user
成員設定為客戶端使用者名稱;這會成為 external_user
系統變數的值。
static int auth_simple_proxy_server (MYSQL_PLUGIN_VIO *vio,
MYSQL_SERVER_AUTH_INFO *info)
{
unsigned char *pkt;
int pkt_len;
/* read the password as null-terminated string, fail on error */
if ((pkt_len= vio->read_packet(vio, &pkt)) < 0)
return CR_ERROR;
/* fail on empty password */
if (!pkt_len || *pkt == '\0')
{
info->password_used= PASSWORD_USED_NO;
return CR_ERROR;
}
/* accept any nonempty password */
info->password_used= PASSWORD_USED_YES;
/* if authentication string is nonempty, use as proxied user name */
/* and use client name as external_user value */
if (info->auth_string_length > 0)
{
strcpy (info->authenticated_as, info->auth_string);
strcpy (info->external_user, info->user_name);
}
return CR_OK;
}
成功連線後,USER()
函數應指示連線的客戶端使用者和主機名稱,而 CURRENT_USER()
應指示在工作階段期間套用其權限的帳戶。如果沒有發生代理,則後者值應為連線的使用者帳戶;如果發生代理,則應為被代理的帳戶。
編譯並安裝插件,然後測試它。首先,以 plugin_user1
身分連線
$> mysql --user=plugin_user1 --password
Enter password: x
在這種情況下,不應發生代理
mysql> SELECT USER(), CURRENT_USER(), @@proxy_user, @@external_user\G
*************************** 1. row ***************************
USER(): plugin_user1@localhost
CURRENT_USER(): plugin_user1@localhost
@@proxy_user: NULL
@@external_user: NULL
然後以 plugin_user2
身分連線
$> mysql --user=plugin_user2 --password
Enter password: x
在這種情況下,plugin_user2
應被代理到 proxied_user
mysql> SELECT USER(), CURRENT_USER(), @@proxy_user, @@external_user\G
*************************** 1. row ***************************
USER(): plugin_user2@localhost
CURRENT_USER(): proxied_user@localhost
@@proxy_user: 'plugin_user2'@'localhost'
@@external_user: 'plugin_user2'@'localhost'