mysql报错1045,我在两台服务器上追到同一个原因
- 2026年11月10日
- 15 次
登录被拒这件事在 MySQL 里只有一个编号,就是mysql报错1045。它跟权限不足是两件事:库表授权还没轮到判断,服务端连身份都没认下来,连接就被断掉了。判定只看三样,账号名、来源主机、密码,任何一样和 mysql.user 里的记录对不上,返回的都是同一个编号。
编号一样,原因可以完全不同。所以看到 1045 先别急着改密码,把报错行读全,再看账号是从哪条 host 记录上被挡下来的。
ERROR 1045 报错原文与常见成因
遇到mysql报错1045,先把这行读全,别只盯着那个数字。完整的一行长这样:

ERROR 1045 (28000): Access denied for user 'app'@'10.0.3.24' (using password: YES)
括号里的 28000 是 SQLSTATE,1045 是服务端错误号。真正有用的是后半段两处。
'app'@'10.0.3.24' 是服务端看到的来源地址,不是授权表里写的那一行。拿这个 IP 去 mysql.user 的 host 列搜,搜不到很正常。(using password: YES) 表示密码确实发过去了,可以排除「客户端根本没带密码」这一类。
剩下的成因按我遇到的次数排:
- 账号不存在,或者建在了另一台实例上;
- 账号在,host 写的是 localhost,这次却是从内网 IP 连进来的;
- 密码对不上,或者账号的认证插件和客户端不匹配;
- 直接 UPDATE 改过 mysql.user,没执行 flush privileges(8.0 起 mysql.user 是只读视图,这条路已经走不通,只能 CREATE USER / ALTER USER / GRANT)
最后一条要分清:用 CREATE USER、GRANT 改的权限会自动生效,不用手动刷;只有直接改系统表才需要刷。
host 字段决定从哪台机器登录
mysql.user 里的 host 管的不是「你从哪来」,是「允不允许你从这来」。
匹配按精确程度走:localhost 最具体,然后是写死的 IP,再往后是 10.0.3.% 这种网段,最后才是 %。同名账号可以挂好几行,服务端挑最具体的那行去比密码。
这台实例上,app 挂着两行:一行 localhost,一行 10.0.3.%。本地 socket 登录走 localhost 那行,口令是旧的;从 10.0.3.24 连过来,命中的是 10.0.3.% 那行,两行口令不一致,报错就出来了。

两行口令不一致的时候,从 10.0.3.% 那行进来的连接几乎必然报 1045,只改其中一行没用。
第一次撞上是在一台 5.7 上:本机用 root 进去看,账号、库、密码都对,应用那边一直报错。第二次是 8.0,反过来了,应用能连,本地命令行进不去。查到最后都是 host 那一列,只是被挡的是不同的客户端。
三处要对上的东西,按顺序查
顺序不能乱。先确认账号在不在,再看 host,最后才怀疑密码。
第一步,把同名账号的所有行列出来:
SELECT user, host, plugin FROM mysql.user WHERE user = 'app';
输出里一个 host 一行,重点看 host 和 plugin 两列。一行都没有的话,账号就不在这台实例上,后面几步都不用做了。我这边跑出来是两行:
- app | localhost | mysql_native_password
- app | 10.0.3.% | caching_sha2_password
第二行才是远程连接会用到的那条,它的密码和第一行不是一个。
第二步,把报错行里的来源地址对到具体那一行:
报错行写的是 'app'@'10.0.3.24',就拿 10.0.3.24 去对 host 列。localhost 匹配不上,10.0.3.% 匹配得上,% 也匹配得上,按最具体优先,命中 10.0.3.%。这一步只用看表,连接是断的也能做。
SELECT user, host, plugin FROM mysql.user
WHERE user = 'app' AND '10.0.3.24' LIKE host;
把来源 IP 当成待匹配串、host 当模式,能把候选行筛出来:localhost 这行不成立,10.0.3.% 和 % 两行都成立,最后按最具体优先取 10.0.3.%。
第三步,把口令改成一致:
ALTER USER 'app'@'10.0.3.%' IDENTIFIED BY '和 localhost 那行相同的口令';
GRANT SELECT, INSERT, UPDATE ON shop.* TO 'app'@'10.0.3.%';
这里不能用 CREATE USER —— 上一节列出来的两行里 10.0.3.% 已经存在,重复建会直接抛 ERROR 1396 (HY000): Operation CREATE USER failed for 'app'@'10.0.3.%'。账号不存在时才建,存在就用 ALTER USER 改口令。
把 host 写成 10.0.3.% 比给每台机器单独建账号省事,代价是这一段内网里的机器共用同一份授权,谁拿到口令都能进。
所以在动手改授权之前,先把mysql报错1045里那个来源地址抄下来,后面每一步都拿它对照。

建完直接重连,不用重启 mysqld。换 host 再连一次,写成 mysql -h 10.0.3.24 -P 3306 -u app -p shop。
回车之后的变化很明确:提示从 ERROR 1045 变成 Welcome to the MySQL monitor,接着 SHOW DATABASES 能看到 shop。
进去之后还有一步值得做,执行 SELECT CURRENT_USER();,返回 app@10.0.3.%,这就是实际生效的那条记录。它和 USER() 不是一个东西:后者是你写在命令里的账号名和来源,前者是服务端匹配到的授权行,两个值对不上,说明还在别的 host 上。
localhost 和 % 差在哪
这两个写法不是「本机」和「别的机器」这么简单,它们对应两条不同的连接路径。
localhost 走 Unix socket,不经过 TCP。所以在 Linux 上 mysql -h localhost 和 mysql -h 127.0.0.1 是两条路:前者匹配 localhost 那行,后者走 TCP,匹配 127.0.0.1 或 %,匹配不到 localhost。
用 -h 127.0.0.1 去测「本地能不能连」,测出来的结果和 socket 登录不是一回事。
想确认走的是哪条路,看 socket 文件在不在最直接。常见的路径是 /var/run/mysqld/mysqld.sock 或者 /tmp/mysql.sock,具体位置由 my.cnf 里的 socket 项决定,以自己机器上的为准。
% 匹配任意来源,但它最不具体。两行都在的时候,本机 socket 永远先命中 localhost;只有 % 一行的时候,socket 登录也能进,因为 % 把 localhost 也覆盖了。
分清这两条路之后,多数mysql报错1045在第 3 步就结束了。
还没定下来的那两处
账号放在代理或中间件后面,服务端看到的来源是代理 IP。host 该写代理段还是写真实网段,取决于代理有没有透传源地址。我手上这两台是直连的,没这套环境,测不出来。
另一处是 skip_name_resolve。开了它,服务端不对客户端 IP 做反解,host 只能按 IP 或通配符匹配,写主机名的那几条会全部失效。它在 my.cnf 里,改完要重启 mysqld,值不值得改要看你这边 DNS 稳不稳。
若非作者标注皆为李涛自留地原创文章,转载或复制请以超链接形式并注明出处。
- html文本框的基本语法 Html文本框如何使用? 2022-06-22
- PHPCMS中Tags标签伪静态步骤:PHPCMS首页、文章页调用tags标签的方法 2022-11-13
- Phpstudy环境下登录PhpMyadmin报错(HY000/1045) 2021-11-08
- 宝塔面板网站502 Bad Gateway报错的N种原因和解决方法 2026-07-02
- phpcms栏目和文章Id从1开始 2021-11-17
- PHP explode() 和 implode() 函数详解:字符串与数组互转 2026-07-02
