擴充 MySQL 9.0  /  ...  /  在身份驗證外掛程式中實作代理使用者支援

4.4.9.4 在身份驗證外掛程式中實作代理使用者支援

可插拔身份驗證所實現的功能之一是代理使用者 (請參閱代理使用者)。若要讓伺服器端身份驗證外掛程式參與代理使用者支援,必須滿足以下條件

  • 當連線用戶端應被視為代理使用者時,外掛程式必須在 MYSQL_SERVER_AUTH_INFO 結構的 authenticated_as 成員中傳回不同的名稱,以指示被代理的使用者名稱。它也可以選擇性地設定 external_user 成員,以設定 external_user 系統變數的值。

  • 必須設定代理使用者帳戶,以便透過外掛程式進行驗證。使用 CREATE USERGRANT 陳述式,將帳戶與外掛程式建立關聯。

  • 代理使用者帳戶必須具有被代理帳戶的 PROXY 權限。使用 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'

當伺服器調用外掛程式以驗證用戶端時,它會將適當的身份驗證字串傳遞給外掛程式。外掛程式負責

  1. 將字串剖析為其組件,以判斷要使用的對應

  2. 將用戶端使用者名稱與對應進行比較

  3. 傳回正確的 MySQL 使用者名稱

例如,如果 extuser2example.com 主機連線,則伺服器會將 'extuser1=mysqlusera, extuser2=mysqluserb' 傳遞給外掛程式,而外掛程式應該將 mysqluserb 複製到 authenticated_as 中,並以空位元組終止。如果 extuser2example.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'