MySQL不支持CIDR(如192.168.1.0/24),因Host字段仅接受字符串通配符%和_,不解析子网掩码;'192.168.1.%'可匹配该C段IP是因其前缀字面匹配,而CIDR语法直接报错。

MySQL 不支持 CIDR 子网掩码(如 192.168.1.0/24),只能用 % 通配符模拟网段,且匹配逻辑严格依赖字典序和长度,不是“模糊匹配”。
为什么 192.168.1.% 能匹配但 192.168.1.0/24 会报错
MySQL 的 Host 字段只接受字符串通配符:%(任意字符序列)和 _(单个字符),不解析 CIDR。写 'user'@'192.168.1.0/24' 会被当作文本字面量,导致连接时 Host 不匹配,直接拒绝。
-
192.168.1.%→ 匹配192.168.1.1、192.168.1.255、192.168.1.100:33060(端口不影响匹配) -
192.168.1._→ 只匹配形如192.168.1.x的单字符结尾 IP(几乎无实用价值) -
192.168.%→ 匹配192.168.0.1、192.168.255.255,范围比预期大得多
GRANT 语句中如何安全使用 % 模拟网段
必须确保目标 IP 真实落在通配范围内,且没有更具体的冲突规则干扰匹配顺序。
- 先查现有规则:执行
SELECT User, Host FROM mysql.user ORDER BY LENGTH(Host) DESC, Host;—— MySQL 按Host字符串长度降序、再字典序升序排序,越长越优先(192.168.1.100优先于192.168.1.%) - 避免冗余
%记录:如果已有'app'@'%',再加'app'@'192.168.1.%'也无效——前者已覆盖,且更短,但因排序靠后反而可能被跳过;应删掉泛化记录 - 授权示例(仅限 C 段):
GRANT SELECT, INSERT ON appdb.* TO 'api'@'192.168.1.%' IDENTIFIED BY 's3cr3t'; - 不要省略
IDENTIFIED BY:即使用户已存在,GRANT ... TO 'u'@'h'不会重置密码,旧密码仍生效;若需统一密码,显式加上
批量授权多个 IP 段的实操建议
没有原生批量语法,得靠脚本生成 SQL 或分批执行。关键点是避免权限叠加混乱和 Host 冲突。
- 确认所有目标网段互不重叠:比如
192.168.1.%和192.168.1.100共存没问题,但192.168.%和192.168.1.%同时存在时,后者优先——只要它有权限,前者就无关紧要 - 用 shell 一键生成(以三个网段为例):
for net in "10.0.1" "10.0.2" "172.16.5"; do echo "GRANT SELECT ON myapp.* TO 'ro'@'${net}.%' IDENTIFIED BY 'ro123';"; done | mysql -u root -p - MySQL 8.0+ 注意认证插件:如果客户端是老版本(如 5.7 client),连
'ro'@'10.0.1.%'可能报Authentication plugin 'caching_sha2_password' cannot be loaded,需加ALTER USER 'ro'@'10.0.1.%' IDENTIFIED WITH mysql_native_password BY 'ro123';
验证是否真生效:别只看 SHOW GRANTS
SHOW GRANTS FOR 'u'@'h' 只显示该记录的权限,不反映实际连接结果。真正决定能否连上的,是 MySQL 在握手时对 TCP 源 IP 和 User@Host 的实时匹配。
- 从目标机器连一次:
mysql -h your-mysql-ip -u api -p -e "SELECT USER(), CURRENT_USER();"——CURRENT_USER()返回匹配到的User@Host,这才是真实生效的账号 - 如果返回
api@192.168.1.50,说明命中了具体 IP 记录;若返回api@192.168.1.%,说明走的是通配规则 - 若连不上,优先检查防火墙(
iptables/firewalld)是否放行 3306 端口,而不是反复改权限
最易被忽略的一点:Host 匹配发生在认证前,哪怕密码正确,只要 User@Host 没匹配上,就会报 Access denied for user 'u'@'x.x.x.x' —— 这里的 x.x.x.x 是客户端真实源 IP,不是你配置的通配符,它提醒你该 IP 没落在任何一条 Host 规则里。


















