mysql只允许指定IP访问数据库_mysql访问控制方法

来源:这里教程网 时间:2026-02-28 20:48:45 作者:

MySQL 用户账号的 host 字段决定谁能连上

MySQL 不是靠防火墙或配置文件全局限制 IP,而是把访问权限“绑”在每个

user@host
账号上。
host
字段值直接控制允许连接的来源 IP 或域名。比如创建用户时写
'app_user'@'192.168.1.100'
,就只允许该 IP 连接;用
'app_user'@'192.168.1.%'
可匹配整个 C 段。

常见错误是创建用户时用了

'app_user'@'%'
,结果任何网络位置都能连——这在生产环境极危险。

新建用户务必显式指定最小必要范围的
host
,如具体 IP、内网网段或容器服务名(Docker 环境中可用
'app_user'@'web_app'
已有用户想收紧权限,不能只改密码,必须用
RENAME USER
或先
DROP USER
再重建,因为
ALTER USER ... IDENTIFIED BY
不修改
host
host
'localhost'
是特殊值:它走 Unix socket,不走 TCP/IP,和
'127.0.0.1'
不等价;后者走 TCP,受网络层规则约束

GRANT 语句必须带明确 host 才生效

执行

GRANT
时如果漏写
@'xxx'
,MySQL 默认按当前会话的
user()
中的 host 解析,极易误授给
'%'
—— 这是线上事故高频原因。

正确做法永远显式写出完整账号标识:

GRANT SELECT, INSERT ON mydb.users TO 'app_user'@'10.0.2.5';

之后别忘了

FLUSH PRIVILEGES;
,否则权限不会立即生效(虽然多数情况下 MySQL 会自动重载,但显式刷新更可靠)。

避免用
GRANT ... TO 'app_user'@'%' IDENTIFIED BY ...
,除非你真需要全网可连
若需多个 IP,得逐个授权:
GRANT ... TO 'app_user'@'10.0.2.5'; GRANT ... TO 'app_user'@'10.0.2.6';
MySQL 8.0+ 支持角色(ROLE),可先建角色再把权限赋给角色,最后把角色授予不同
user@host
,便于批量管理

bind-address 和防火墙是第二道防线

bind-address
my.cnf
里控制 mysqld 监听哪个网卡。设成
127.0.0.1
就只响应本地连接;设成
0.0.0.0
(默认)则监听所有接口——此时单靠账号
host
限制是唯一有效手段。

但仅靠 MySQL 权限不够稳健:万一账号被误配成

@'%'
,或有人拿到 root 密码,就彻底失守。所以建议叠加系统级防护:

Linux 上用
iptables
nftables
限制只允许特定源 IP 访问 3306 端口
云环境优先用安全组(Security Group),比数据库内建权限更前置、更难绕过 Docker/K8s 场景下,避免将 MySQL 容器端口映射到宿主机(
-p 3306:3306
),改用内部网络通信

验证是否真的限制住了

别只信配置文件和 GRANT 语句,一定要从目标客户端机器实测。常用验证方式:

从被允许的 IP 运行:
mysql -h db.example.com -u app_user -p
,应成功
从被禁止的 IP 运行同样命令,应报错
ERROR 1045 (28000): Access denied for user
ERROR 2003 (HY000): Can't connect to MySQL server
(后者说明被防火墙/网络层拦截)
登录 MySQL 后查
SELECT User, Host FROM mysql.user;
,确认没有残留的
@'%'
或宽泛网段账号
注意:MySQL 的权限检查顺序是 “最具体匹配优先”,比如同时存在
'user'@'192.168.1.100'
'user'@'192.168.1.%'
,前者会优先生效

真正容易被忽略的是权限缓存行为和 host 匹配的细节逻辑——比如 DNS 反向解析开启时,

host
值可能被替换成域名,导致预期外的匹配失败。生产库建议关闭
skip-name-resolve
并坚持用 IP 授权。

相关推荐