可插入身份验证使之成为可能的功能之一是代理用户(请参阅代理用户)。对于服务器端身份验证插件参与代理用户支持,必须满足以下条件:
当连接客户端应被视为代理用户时,插件必须
authenticated_as
在结构成员中 返回不同的名称MYSQL_SERVER_AUTH_INFO
,以指示代理用户名。它还可以选择性地设置external_user
成员,以设置external_user
系统变量的值。代理用户帐户必须设置为由插件进行身份验证。使用
CREATE USER
orGRANT
语句将帐户与插件相关联。
换句话说,插件所需的代理用户支持的唯一方面是将其设置
authenticated_as
为代理用户名。其余部分是可选的(设置
external_user
)或由 DBA 使用 SQL 语句完成。
身份验证插件如何确定代理用户连接时返回哪个代理用户?这取决于插件。通常,插件根据服务器传递给它的身份验证字符串将客户端映射到代理用户。该字符串来自声明中指定使用插件进行身份验证
的子句
AS
部分。IDENTIFIED WITH
CREATE USER
插件开发人员确定身份验证字符串的语法规则并根据这些规则实现插件。假设一个插件采用逗号分隔的成对列表,将外部用户映射到 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'