6.2.18 基于 SQL 的帐户活动审核
应用程序可以使用以下准则来执行将数据库活动与 MySQL 帐户相关联的基于 SQL 的审核。
MySQL 帐户对应于mysql.user
系统 table 中的行。Client 端成功连接后,服务器会将 Client 端认证到该 table 中的特定行。该行中的User
和Host
列值唯一地标识该帐户,并与'user_name'@'host_name'
格式相对应,在该格式中,在 SQL 语句中写入了帐户名。
用于验证 Client 端的帐户确定该 Client 端具有哪些特权。通常,可以调用CURRENT_USER()函数来确定该帐户适用于 Client 用户。它的值是从该帐户user
table 行的User
和Host
列构造的。
但是,在某些情况下CURRENT_USER()值不对应于 Client 端用户,而是对应于另一个帐户。当特权检查不是基于 Client 的帐户时,会发生以下情况:
-
使用
SQL SECURITY DEFINER
特性定义的存储例程(过程和函数) -
使用
SQL SECURITY DEFINER
Feature 定义的视图 -
触发器和事件
在这些情况下,对DEFINER
帐户执行特权检查,而CURRENT_USER()则引用该帐户,而不是调用存储例程或视图或导致触发触发器的 Client 端帐户。要确定调用用户,可以调用USER()函数,该函数返回一个值,该值指示 Client 端提供的实际用户名以及 Client 端从中连接的主机。但是,此值不一定直接与user
table 中的帐户相对应,因为USER()值从不包含通配符,而帐户值(由CURRENT_USER()返回)可以包含用户名和主机名通配符。
例如,空白用户名与任何用户匹配,因此''@'localhost'
帐户使 Client 端可以使用任何用户名从 localhost 作为匿名用户连接。在这种情况下,如果 Client 端以user1
的身份从 localhost 连接,则USER()和CURRENT_USER()返回不同的值:
mysql> SELECT USER(), CURRENT_USER();
+-----------------+----------------+
| USER() | CURRENT_USER() |
+-----------------+----------------+
| user1@localhost | @localhost |
+-----------------+----------------+
帐户的主机名部分也可以包含通配符。如果主机名包含'%'
或'_'
模式字符或使用网络掩码符号,则该帐户可用于从多个主机连接的 Client 端,并且CURRENT_USER()值不会指示哪个主机。例如,user2
可以使用帐户'user2'@'%.example.com'
从example.com
域中的任何主机进行连接。如果user2
从remote.example.com
连接,则USER()和CURRENT_USER()返回不同的值:
mysql> SELECT USER(), CURRENT_USER();
+--------------------------+---------------------+
| USER() | CURRENT_USER() |
+--------------------------+---------------------+
| [email protected] | user2@%.example.com |
+--------------------------+---------------------+
如果应用程序必须调用USER()进行用户审核(例如,如果从触发器内部进行审核),但是还必须能够将USER()值与user
table 中的帐户相关联,则有必要避免在应用程序中包含通配符的帐户User
或Host
列。特别是,不允许User
为空(这会创建一个匿名用户帐户),也不允许Host
值中包含模式字符或网络掩码符号。所有帐户都必须具有非空的User
值和 LiteralsHost
值。
关于前面的示例,应更改''@'localhost'
和'user2'@'%.example.com'
帐户以不使用通配符:
RENAME USER ''@'localhost' TO 'user1'@'localhost';
RENAME USER 'user2'@'%.example.com' TO 'user2'@'remote.example.com';
如果user2
必须能够从example.com
域中的多个主机进行连接,则每个主机应有一个单独的帐户。
要从CURRENT_USER()或USER()值中提取用户名或主机名部分,请使用SUBSTRING_INDEX()函数:
mysql> SELECT SUBSTRING_INDEX(CURRENT_USER(),'@',1);
+---------------------------------------+
| SUBSTRING_INDEX(CURRENT_USER(),'@',1) |
+---------------------------------------+
| user1 |
+---------------------------------------+
mysql> SELECT SUBSTRING_INDEX(CURRENT_USER(),'@',-1);
+----------------------------------------+
| SUBSTRING_INDEX(CURRENT_USER(),'@',-1) |
+----------------------------------------+
| localhost |
+----------------------------------------+