代码改变世界

MySQL 如何拥有针对特定主机的单一用户账户

  abce  阅读(23)  评论(0编辑  收藏  举报

MySQL 如何拥有针对特定主机的单一用户账户? 通常,我们会创建用户名相同但主机/IP 不同的独立用户账户,如 <USER>@<HOST1>, <USER>@<HOST2> ....。<USER>@<HOSTn>。然后,给予这些用户相同的授权(权限/角色)和设置(密码、SSL 等)。

 

除了以上的做法,可以创建一个可以从所有主机连接的单一用户(''@'%'),然后使用 init_connect 服务器参数调用一个存储过程来实现对用户的主机限制。这将使管理用户账户设置和权限变得更容易,因为只需将其应用于一个数据库用户账户。

 

 

以下是执行过程。

 

客户端主机名和 IP:

1
2
db1    192.168.137.101
db2    192.168.137.102

创建一个 schema,该 schema 中一个表包含所有允许访问的主机列表。该表将与 USER() 函数的输出进行校验。

1
2
3
4
5
6
7
8
create database if not exists logins;
use logins;
create table allow_list(
  user varchar(32)  not null comment 'mysql.user.user',
  host varchar(255) not null comment 'mysql.user.host',
  remarks  varchar(100),
  primary key (user, host)
);

创建存储过程,针对 host = '%' 的值(来自 CURRENT_USER())并在 allow_list 表(基于 USER())中找到的用户实施主机限制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
DELIMITER //
CREATE PROCEDURE sp_init_connect(IN p_current_user_host VARCHAR(300))
SQL SECURITY DEFINER
BEGIN
  DECLARE v_message_text  VARCHAR(128);
  DECLARE v_exists    BOOLEAN   DEFAULT 0;
  DECLARE v_user      VARCHAR(32) ;
  DECLARE v_host      VARCHAR(255) ;
  DECLARE c_host      VARCHAR(255) ;
  SET v_user  = SUBSTRING_INDEX(USER(), '@', 1);
  SET v_host  = SUBSTRING_INDEX(USER(), '@', -1);
  SET c_host  = SUBSTRING_INDEX(p_current_user_host, '@', -1);
  SELECT 1 INTO v_exists  FROM allow_list WHERE user = v_user AND  host = v_host LIMIT 1;
  IF c_host = '%' THEN  /* Only for users that can connect from any host */
    IF NOT v_exists THEN
      SET v_message_text=CONCAT('User not in ', database(), '.allow_list CURRENT_USER[', p_current_user_host, ']'); /* Set the error message as short as possible so it does not get truncated in the log. */
      SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = v_message_text, MYSQL_ERRNO = 1001;
    END IF;
  END IF;
END;
//
DELIMITER ;

CURRENT_USER() 返回服务器用于验证当前客户端的 MySQL 帐户的用户名和主机名组合(存储在系统表mysql.user中的账户)。该账户决定你的访问权限。返回值是以 utf8mb3 字符集表示的字符串。USER() 以 utf8mb3 字符集字符串形式返回当前 MySQL 用户名和主机名。该值表示你连接服务器时指定的用户名,以及连接的客户端主机。CURRENT_USER() 的值可能与 USER() 的值不同。例如,从主机11.11.11.21 远程登录数据库,执行以下查询:

1
2
3
4
5
6
7
8
9
mysql> select user(),current_user();
+-------------------------------+------------------------+
| user()                        | current_user()         |
+-------------------------------+------------------------+
| myabc_seoole_user@11.11.11.21 | myabc_seoole_user@10.% |
+-------------------------------+------------------------+
1 row in set (0.02 sec)
 
mysql>

在存储过程中或者视图中,CURRENT_USER()返回的是定义该对象的用户账户(即 definer 的值);如果使用了 sql security invoker,返回的就是调用者, USER() 或者 SESSION_USER() 的值。

 

设置服务器参数 init_connect:

1
SET GLOBAL init_connect = "CALL logins.sp_init_connect(CURRENT_USER())";

并将该参数写入配置文件:

1
2
[mysqld]
init_connect = 'CALL logins.sp_init_connect(CURRENT_USER())'

接着创建数据库账户并授权:

1
2
3
4
CREATE USER 'use1'@'%'     IDENTIFIED BY 'abcd1234' ;
CREATE USER 'use2'@'192.%' IDENTIFIED BY 'abcd1234' ;
GRANT EXECUTE ON logins.* TO 'use1'@'%';
GRANT EXECUTE ON logins.* TO 'use2'@'192.%';

将主机映射加入hosts文件。如果有dns,可有在dns中配置

1
192.168.137.102  db2.example.com

 

尝试使用'use1'@'%'连接数据库。

因为 allow_list 表目前还是空表,因此尝试连接应该失败。

从db1(192.168.137.101)尝试连接:

1
2
3
[root@db1 ~]# mysql -uuse1 -pabcd1234 -h192.168.122.1 -P3306 -e'SELECT USER(), CURRENT_USER(), @@versionG'
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query

从db2(192.168.137.102)尝试连接:

1
2
3
[root@db2 ~]# mysql -uuse1 -pabcd1234 -h192.168.122.1 -P3306 -e'SELECT USER(), CURRENT_USER(), @@versionG'
mysql: [Warning] Using a password on the command line interface can be insecure.
ERROR 2013 (HY000) at line 1: Lost connection to MySQL server during query

检查错误日志,这需要将log_error_verbosity 设置成 2 或者 3。

1
2
2024-06-28T08:40:56.469085Z 12 [Warning] [MY-013130] [Server] Aborted connection 12 to db: 'unconnected' user: 'use1' host: '192.168.137.101' (init_connect command failed; diagnostics area: MY-001001 - User not in logins.allow_list CURRENT_USER[use1@%])
2024-06-28T08:41:22.064094Z 13 [Warning] [MY-013130] [Server] Aborted connection 13 to db: 'unconnected' user: 'use1' host: 'db2.example.com' (init_connect command failed; diagnostics area: MY-001001 - User not in logins.allow_list CURRENT_USER[use1@%])

插入两条记录后,再次尝试连接:

1
2
INSERT INTO allow_list(user, host) VALUES('use1', '192.168.137.101');
INSERT INTO allow_list(user, host) VALUES('use1', 'db2.example.com');

 

从db1(192.168.137.101)尝试连接:

1
2
3
4
5
6
[root@db1 ~]# mysql -uuse1 -pabcd1234 -h192.168.122.1 -P3306 -e'SELECT USER(), CURRENT_USER(), @@versionG'
mysql: [Warning] Using a password on the command line interface can be insecure.
*************************** 1. row ***************************
USER(): use1@192.168.137.101
CURRENT_USER(): use1@%
@@version: 8.0.32

从db2(192.168.137.102)尝试连接:

1
2
3
4
5
6
[root@db2 ~]# mysql -uuse1 -pabcd1234 -h192.168.122.1 -P3306 -e'SELECT USER(), CURRENT_USER(), @@versionG'
mysql: [Warning] Using a password on the command line interface can be insecure.
*************************** 1. row ***************************
USER(): use1@db2.example.com
CURRENT_USER(): use1@%
@@version: 8.0.32

 

尝试使用'use2'@'192.%'连接数据库。

从db1(192.168.137.101)尝试连接:

1
2
3
4
5
6
[root@db1 ~]# mysql -uuse2 -pabcd1234 -h192.168.122.1 -P3306 -e'SELECT USER(), CURRENT_USER(), @@versionG'
mysql: [Warning] Using a password on the command line interface can be insecure.
*************************** 1. row ***************************
USER(): use2@192.168.137.101
CURRENT_USER(): use2@192.%
@@version: 8.0.32

allow_list表中没有'use2@'192.168.137.101','use2@'192.%'也验证通过了。

相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)
历史上的今天:
2023-07-24 LSM树学习笔记(2)
2022-07-24 【MySQL】relaylog损坏后,如果解决"Error Reading Relay Log Event"
2016-07-24 DG - logical standby switchover切换过程
点击右上角即可分享
微信分享提示