如何修复 MySQL 错误 1045 Access Denied:完整指南

很少有数据库错误能像 ERROR 1045 (28000): Access denied for user 'root'@'localhost' 这样普遍出现,又这样频繁被误诊。当你的应用、迁移脚本或命令行尝试认证的那一刻,MySQL 拒绝了连接,整个请求随之失败。与表示 MySQL 根本没有应答的 "connection refused"(连接被拒绝)错误不同,错误 1045 是一次认证失败:TCP 连接已成功建立,服务器接受了握手,然后才拒绝了你提交的凭证。

这一区别很重要,因为它准确地告诉了你去哪里找问题。网络没问题,端口是开放的,mysqld 也在运行——问题出在 MySQL 的授权表里。七个常见嫌疑是:密码错误、用户不存在、你连接的来源主机与 mysql.user 允许的不匹配、MySQL 8.0 引入的 caching_sha2_password 插件、从未刷新的权限、遮蔽真实账号的匿名 ''@'localhost' 用户,以及现代发行版对 root 登录的限制。本指南逐一讲解每个原因及解决它的确切命令。

什么是 MySQL 错误 1045?

MySQL 错误 1045 由服务器的认证子系统在 TCP 连接已建立之后返回。完整的消息通常写作 ERROR 1045 (28000): Access denied for user 'user'@'host' (using password: YES)。SQLSTATE 28000 是"无效授权规范"的标准代码,结尾的 (using password: YES)(或 NO)告诉你客户端到底有没有发送密码——当故障是连接串里漏填密码而非密码错误时,这是一条很有价值的线索。

由于认证发生在传输层之后,错误 1045 证明服务器可达且正在监听。你不应浪费时间检查防火墙、bind-addressmysqld 是否在运行;那些会产生错误 2002 或 2003,而不是 1045。相反,应聚焦于 MySQL 每次登录校验的三元组:用户名、连接来源的主机、以及密码(包括对其进行哈希的插件)。三者中任何一个不匹配都会返回 1045。

一个细节:MySQL 先匹配主机再匹配密码。如果你以 appuser10.0.0.5 连接,但授权表里只有 'appuser'@'localhost',MySQL 可能报告 1045 而非 1130,因为它根本没找到能校验密码的匹配行。这就是为什么 Host 列是首先要检查的。

常见原因

错误 1045 可追溯到一小撮可预测的根本原因。识别哪一条适用于你的情况,通常比反复试着重置密码更快:

分步修复指南

步骤 1:确认确切错误并识别用户和主机

阅读完整的错误消息,注意 MySQL 报告的用户和主机,而不是你以为自己在用的那个。消息里引用的 'user'@'host' 是 MySQL 尝试匹配的身份。用命令行复现故障,这样你能控制每一个参数:

mysql -u root -p
# 或指定用户和主机:
mysql -h 127.0.0.1 -P 3306 -u appuser -p appdb

如果消息以 (using password: NO) 结尾,说明客户端从未发送密码——在做任何其他事之前,先检查连接串里是否漏填或留空了密码字段。

步骤 2:验证用户是否存在并检查允许的主机

以管理员身份登录并检查授权表。Host 列绊倒了大多数人:仅定义为 localhost 的用户无法从远程地址连接。

sudo mysql   # 在 root 使用 auth_socket 的 Ubuntu 上有效
# 然后在 MySQL 内:
SELECT User, Host, plugin FROM mysql.user WHERE User = 'appuser';

-- 如果没有返回行,说明用户不存在。创建它:
CREATE USER 'appuser'@'%' IDENTIFIED BY 'a-strong-password';

如果用户仅以 'appuser'@'localhost' 存在而你远程连接,请创建一个匹配的 'appuser'@'%' 账号——或更好的是,限定到特定子网,例如 'appuser'@'10.0.0.%'

步骤 3:使用 ALTER USER 重置密码

当密码错误或未知时,用 ALTER USER 显式重置。这是对已弃用的 SET PASSWORD 语法的现代推荐替代:

ALTER USER 'appuser'@'%' IDENTIFIED BY 'new-strong-password';
FLUSH PRIVILEGES;

如果你完全丢失了 root 密码,用 --skip-grant-tables 重启 MySQL 以绕过认证,重置 root,然后正常重启:

sudo systemctl stop mysql
sudo mysqld_safe --skip-grant-tables --skip-networking &
mysql -u root
# 在 MySQL 内运行:
#   ALTER USER 'root'@'localhost' IDENTIFIED BY 'new-root-password';
#   FLUSH PRIVILEGES;
sudo systemctl start mysql

步骤 4:修复认证插件不匹配

MySQL 8.0+ 默认为新用户使用 caching_sha2_password。如果你的客户端或驱动不支持它,即使密码正确也会出现错误 1045。将该用户切换到旧插件,或——更推荐——升级客户端:

-- 将用户切回旧插件(老客户端的快速修复)
ALTER USER 'appuser'@'%' IDENTIFIED WITH mysql_native_password
  BY 'a-strong-password';

-- 或在 my.cnf 中为新用户设置全局默认值:
-- [mysqld]
-- default_authentication_plugin = mysql_native_password

优先升级客户端而非降级安全性:caching_sha2_password 更快且使用 SHA-256。旧插件只应作为你无法立即升级的客户端的临时兼容补丁使用。

步骤 5:清除匿名用户冲突

许多默认安装会创建匿名 ''@'localhost' 账号。MySQL 按顺序匹配授权行,空用户行可能抢先于真实账号匹配成功,导致一个你确信密码正确却出现 1045 的令人困惑的错误。删除它:

-- 查找匿名用户
SELECT User, Host FROM mysql.user WHERE User = '';

-- 删除它们
DROP USER ''@'localhost';
DROP USER ''@'localhost.localdomain';
FLUSH PRIVILEGES;

步骤 6:刷新权限并测试连接

在 MySQL 5.7 及更早版本上,对授权表的直接修改在重新加载之前不会生效。尽管现代版本的 CREATE USERALTER USERGRANT 会自动重载,但刷新是无害的,能排除授权表过期这一原因:

FLUSH PRIVILEGES;

-- 验证该账号生效的授权
SHOW GRANTS FOR 'appuser'@'%';
# 最后,从应用使用的确切主机测试:
mysql -h db.example.com -P 3306 -u appuser -p appdb

MySQL 诊断命令

这些命令构成了错误 1045 的完整诊断闭环。按顺序运行它们,以精确定位失败的用户、主机和插件三元组:

# 1. 以 root 连接(Ubuntu/Debian 的 auth_socket 允许 sudo)
sudo mysql -u root

# 2. 交互式测试特定用户的登录
mysql -u appuser -p appdb
-- 3. 列出每个账号、其主机和认证插件
SELECT User, Host, plugin FROM mysql.user;

-- 4. 显示授予特定账号的权限
SHOW GRANTS FOR 'appuser'@'%';

-- 5. 检查当前生效的默认认证插件
SHOW VARIABLES LIKE 'default_authentication_plugin';

-- 6. 确认你当前以谁的身份认证
SELECT CURRENT_USER(), USER();

CURRENT_USER()(匹配到的授权表身份)与 USER()(你请求的身份)之间的差异是一条强有力的线索:如果两者不同,说明是匿名或通配符主机行匹配了你的连接,而非你想要的账号。

快速参考表

症状 原因 修复
仅单个用户出现 ERROR 1045 密码错误或已轮换 ALTER USER ... IDENTIFIED BY
Ubuntu 上 root@localhost 出现 1045 auth_socket 插件阻止密码登录 sudo mysql 或切换插件
本地可连,远程出现 1045 用户仅定义为 'user'@'localhost' 创建 'user'@'%' 或特定主机
升级到 MySQL 8 后出现 1045 客户端不支持 caching_sha2_password ALTER USER ... IDENTIFIED WITH mysql_native_password
运行 GRANT 后立即出现 1045 权限未重载(5.7 及更早) FLUSH PRIVILEGES
密码正确仍被拒绝 匿名 ''@'localhost' 遮蔽 DROP USER ''@'localhost'

常见问题

如何重置遗忘的 MySQL root 密码?

停止 MySQL,用 --skip-grant-tables --skip-networking 重启,无密码以 root 连接,运行 ALTER USER 'root'@'localhost' IDENTIFIED BY 'newpassword';,然后正常重启 MySQL。在 systemd 发行版上,重启服务前用 sudo systemctl set-environment MYSQLD_OPTS="--skip-grant-tables" 可以获得干净的生命周期管理。

为什么 MySQL 8 会拒绝在 MySQL 5.7 上正常工作的用户?

MySQL 8.0 将默认认证插件从 mysql_native_password 改为 caching_sha2_password。在 8.0 上新建的用户获得新插件,而无法进行 SHA-2 握手的老客户端即使密码正确也会收到错误 1045。要么升级客户端库,要么用 ALTER USER ... IDENTIFIED WITH mysql_native_password 切换用户。

使用 mysql_native_password 而非 caching_sha2_password 安全吗?

作为兼容措施是可以接受的,但 caching_sha2_password 更安全(SHA-256)且凭借内存缓存在重复登录时更快。旧插件只应为你无法立即升级的客户端使用,且应把在 my.cnf 中设置 default_authentication_plugin = mysql_native_password 视为临时过渡而非长期策略。

为什么运行 GRANT 之后仍然出现 1045?

有三个原因。第一,在 MySQL 8.0 中 GRANT 无法作用于不存在的用户——先用 CREATE USER 创建。第二,你可能授予了 'user'@'localhost' 却从其他主机连接。第三,匿名 ''@'localhost' 行可能在你想要的用户之前匹配。检查 SELECT User, Host FROM mysql.user; 并运行 FLUSH PRIVILEGES; 以排除授权表过期。

总结

MySQL 错误 1045 是认证层的拒绝,这是好消息:它意味着你的服务器可达,修复完全在授权表里。逐一走完 MySQL 每次登录校验的三元组——用户名、主机和密码(及其哈希插件)——原因几乎总会自己浮现。确认确切错误和主机、验证用户存在且 Host 值正确、用 ALTER USER 重置密码、解决任何 caching_sha2_password 不匹配、删除遮蔽真实账号的匿名用户,最后以 FLUSH PRIVILEGES 和一次真实连接测试收尾。

最常见的错误是把 1045 当成网络问题。它不是。如果你能到达端口,问题就在凭证或授权上——而上面的命令能在几分钟而非几小时内解决绝大多数情况。

相关指南