1. 报错现场:Navicat 连接 MySQL 8.x 时突然罢工
如果你最近刚装好 MySQL 8.x,或者接手了一个用了新版 MySQL 的项目,打开 Navicat 点连接时大概率会撞上下面这行红字:
2059 - authentication plugin 'caching_sha2_password' cannot be loaded:翻译成人话就是:MySQL 服务端说“我用的密码认证方式你 Navicat 不支持,咱俩谈不拢”。第一次遇到的朋友很容易懵,明明账号密码没错、端口也通、服务也在跑,为什么就是连不上?
先说结论:这跟账号密码无关,纯粹是 MySQL 8.0 开始把默认的身份认证插件从mysql_native_password换成了caching_sha2_password,而你本地的 Navicat 版本太老,只认旧插件。服务端换了新锁,客户端还在用旧钥匙,自然插不进去。
这个报错我在好几个环境里都真实遇到过——本机连、远程连、Docker 起的 MySQL 都出现过。这篇文章就把三种主流的解决思路完整写一遍,包括底层原理、操作步骤、踩坑点,以及什么情况下选哪种方案最合适。
2. 为什么会有这个错误:认证插件切换的前因后果
2.1 MySQL 8.0 的默认认证机制变化
MySQL 8.0 发布时,官方把默认的身份认证插件从mysql_native_password换成了caching_sha2_password。这俩都是用来验证客户端连接时密码是否正确的东西,但实现方式差异很大。
mysql_native_password是 MySQL 5.x 时代的老方案,基于 SHA1 做密码散列。它的优点是实现简单、兼容性好,几乎所有老客户端都支持;缺点是整个认证过程的安全强度一般,如果连接通道没有加密,密码散列存在被截获后离线爆破的风险。
caching_sha2_password基于 SHA256 密码散列,认证流程更复杂,安全强度更高。它还在服务端加了一层缓存机制,第一次认证成功后,后续短时间内的重复连接可以直接走缓存验证,性能上比逐次全量计算要好。听起来是升级,但代价就是老客户端不认识它。
官方在 MySQL 8.0 里把默认插件改成caching_sha2_password,不是拍脑袋。当时 Oracle 的公告里也说得明白:老的 native password 认证方式已经跟不上安全要求了,新版本必须推动整个生态往强认证迁移。于是,凡是 8.0 之后新建的 MySQL 用户,默认都挂在这个新插件上。
2.2 为什么 Navicat 会翻车
Navicat 其实是支持 MySQL 连接的老牌工具,但它的 Windows 老版本(比如 11.x、12.x 早期版本)内置的 MySQL 客户端库还停留在支持mysql_native_password的阶段。服务端换成了caching_sha2_password,客户端这边没有对应的实现,握手时服务端下发插件名称,客户端尝试加载对应的认证插件,发现没有,于是直接抛出 2059。
这就像两个人在对暗号,服务端说“我用的是新暗号”,客户端翻遍自己口袋,只带了旧暗号,谈判直接破裂。
注意:这个报错不是账号密码输错了,也不是防火墙拦截,更不是 MySQL 服务没启动。你换成老的 MySQL 5.7 连接同样的账密参数,一点毛病没有。
2.3 这个错误的常见出现环境
我统计了一下自己遇到和帮别人排查过的场景,大概是这几类:
| 出现场景 | 典型原因 |
|---|---|
| 本机新装 MySQL 8.x,用 Navicat 连 | 新建用户默认用新插件 |
| 从 MySQL 5.7 迁移数据到 8.x,建了新库新用户 | 迁移过程中用户被重建,走了默认插件 |
| Docker 拉取 mysql:8 镜像起服务 | 镜像内默认配置就是新插件 |
| 远程服务器 MySQL 8.x,本地 Navicat 连接 | 本地 Navicat 版本过老 |
| 使用某些管理面板创建的 MySQL 8 实例 | 面板创建用户时没指定旧插件 |
清楚了根因,接下来就看怎么选择解决路径。
3. 解决方案一:把用户的认证插件改回 mysql_native_password(最常用)
这个方案的思路很简单:既然服务端换了新锁,客户端又太老开不了,那就把锁换回旧款。对本地开发环境、测试环境来说完全够用,操作最快,且不需要升级客户端。
3.1 具体操作步骤
用命令行连接 MySQL(比如用mysql -u root -p),然后执行:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '你的密码'; FLUSH PRIVILEGES;如果 root 用户还涉及远程连接,localhost要改成%或者对应的主机段:
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码'; FLUSH PRIVILEGES;如果你想先看看到底哪个用户挂了什么插件,可以查一下:
SELECT user, host, plugin FROM mysql.user;正常情况下输出里会看到:
+------------------+-----------+-----------------------+ | user | host | plugin | +------------------+-----------+-----------------------+ | root | localhost | caching_sha2_password | | mysql.sys | localhost | caching_sha2_password | +------------------+-----------+-----------------------+你把 root 行的 plugin 改成mysql_native_password,Navicat 就能恢复正常连接。
3.2 为什么执行完后 Navicat 立刻能连
因为 Navicat 握手时,服务端会告诉它“这个用户用的是mysql_native_password”,客户端发现自己带了这个插件,直接按老流程走密码验证,一次通过。整个过程不需要重启 MySQL,FLUSH PRIVILEGES只是刷新权限表的缓存,让改动立即生效。
3.3 这个方案有什么弊端
先说清楚,改回旧插件本质上是“向下兼容”,安全强度确实不如caching_sha2_password。如果你是公网环境、生产库、或者合规要求比较严的项目,建议优先考虑升级客户端,而不是让服务端降级。
另外注意,如果你后续用 SQL 重置了这个用户的密码,插件不会自动变回去。比如你执行:
ALTER USER 'root'@'localhost' IDENTIFIED BY '新密码';这条语句在 MySQL 8.0 里会按照default_authentication_plugin的配置重新指定插件,如果全局默认还是caching_sha2_password,那你的用户又会被切回新插件。遇到“刚才还好好的,改完密码又 2059 了”的情况,多半就是这原因。所以改密时得显式带插件名:
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '新密码';3.4 多条命令的完整操作演示
假设你 MySQL 装在本地,root 密码是123456,想彻底解决 Navicat 连接问题,操作是这样的:
mysql -uroot -p # 输入密码登录后,依次执行: ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456'; FLUSH PRIVILEGES; # 验证 SELECT user, host, plugin FROM mysql.user WHERE user = 'root';输出结果应该变成:
+------+-----------+-----------------------+ | user | host | plugin | +------+-----------+-----------------------+ | root | localhost | mysql_native_password | +------+-----------+-----------------------+这时候再打开 Navicat,原来的 2059 就不会出现了。这个方法我用的频率最高,因为它只影响目标用户,不会动全局配置,风险可控。
4. 解决方案二:升级 Navicat 到支持新插件的版本(更符合长期利益)
如果你连接的是正式环境、生产数据库,或者你根本不想动服务端任何配置,那么升级客户端才是正确方向。Navicat 在 15.x 之后的版本里已经加入了对caching_sha2_password的支持。
4.1 为什么升级客户端是更优解
MySQL 官方在 8.0 里默认新插件,并承诺后续版本会持续强化这套认证机制。你如果一直用老 Navicat,每连一个新库都要去改服务端用户插件,长期看维护成本很高。而且服务端用户插件改成旧方案后,一些新版 MySQL 的特性(比如更严格的密码校验策略、双密码支持等)在使用时可能受限。
把客户端升级到支持新插件的版本,相当于一步到位,以后建任何新用户都用默认配置,不用再操心兼容性。
4.2 具体怎么确认你的 Navicat 是否支持
打开 Navicat 的“关于”看版本号。如果你当前是 11.x、12.x 或者 13 早期版本,大概率不支持新插件。直接去官网下载最新版本,安装后替换即可。
升级完连接还是失败的话,可以检查一下是不是配置文件残留或者连接串里指定了旧协议。Navicat 的连接设置里,“高级”选项卡如果有“使用旧版加密”之类的选项,取消勾选。
小技巧:升级前先备份一下 Navicat 的连接配置,位置一般在“工具 -> 选项 -> 配置”里可以导出。不然重装后一堆连接信息手动重建,很烦。
4.3 这条路的坑点
升级 Navicat 后连老 MySQL 5.7 实例通常没问题,它向下兼容旧插件。
但如果你的 Navicat 是破解版或者从非官方渠道下载的,升级前记得先导出连接配置,升级完如果授权文件失效,连接配置也会受牵连。
另外,升级不代表服务端原来已经改成旧插件的用户会自动变回新插件,你需要根据自己的需要决定是否再执行一次切换。
5. 解决方案三:直接改 MySQL 全局默认认证插件(一劳永逸)
前面两个方案是针对单用户的,如果想让这台 MySQL 之后新建的所有用户默认都用mysql_native_password,可以修改服务端配置。
5.1 修改 my.cnf / my.ini
在 MySQL 配置文件里的[mysqld]段落下加一行:
default_authentication_plugin=mysql_native_passwordLinux 下配置文件一般在/etc/mysql/mysql.conf.d/mysqld.cnf或者/etc/my.cnf,Windows 下在 MySQL 的安装目录里找my.ini。改完重启 MySQL:
systemctl restart mysql或者 Windows 服务里重启 MySQL 服务。
重启后,新建用户默认就会用旧插件:
CREATE USER 'test_user'@'localhost' IDENTIFIED BY '123456'; SELECT user, host, plugin FROM mysql.user WHERE user = 'test_user';你会看到新用户已经挂上了mysql_native_password。
5.2 这个方案适合什么场景
只推荐在本地开发环境、内网测试环境使用。生产环境不要说改全局默认,连单用户降级插件都要走审批,最好还是保持默认新插件 + 升级客户端。
另外一个很常见的变体:你用的是云数据库 RDS,控制台登录时没有权限改配置文件,这种情况就别想着改全局了,老老实实按方案一走单用户修改,或者升级客户端。
5.3 一个容易被人忽略的细节
MySQL 8.0.34 之后,官方其实已经将mysql_native_password标记为废弃(deprecated),并且从 8.4 版本开始默认禁用这个插件。如果你用的是非常新的 MySQL 版本,执行ALTER USER ... IDENTIFIED WITH mysql_native_password可能会直接报错提示插件不可用。
这种情况下,你就需要显式启用旧插件,在配置文件里加:
[mysqld] mysql_native_password=ON然后再执行 ALTER USER 语句。说实话,这条路已经是逆着官方方向走了,能用但不够优雅。我的建议是尽量走升级 Navicat 的路子,省心。
6. 权限表视角:root 之外的其他用户遇到 2059 怎么处理
日常开发中,很多项目不会用 root 连接数据库,而是由 DBA 创建专用账号。这些专用账号如果也遇到 2059,处理思路一样,但需要额外注意权限范围。
6.1 只改应用账号,不动 root
应用账号一般长这样:
CREATE USER 'app_user'@'%' IDENTIFIED BY 'AppPassword123'; GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO 'app_user'@'%';如果这个账号连不上,执行:
ALTER USER 'app_user'@'%' IDENTIFIED WITH mysql_native_password BY 'AppPassword123'; FLUSH PRIVILEGES;只改这个账号,不影响 root 和其他用户。
6.2 区分 localhost / % / 具体 IP
MySQL 的用户是“用户名 + 主机”二元组,'app_user'@'localhost'和'app_user'@'%'是两个完全独立的账号。改错主机段,连接还是报错。
排查的时候先执行:
SELECT user, host, plugin FROM mysql.user WHERE user = 'app_user';看清楚连接来源对应的 host 是哪个,再执行对应的 ALTER。比如你在公司内网用 192.168.1.50 连接,但账号只建了'app_user'@'%',那就要改%那条。
6.3 密码策略可能成为隐形门槛
MySQL 8.0 默认启用了密码校验组件,太简单的密码(比如123456)在建用户时直接会被拒掉。但你要是用IDENTIFIED WITH mysql_native_password BY '123456'这种写法,部分版本也会触发密码强度校验失败。
遇到ERROR 1819 (HY000): Your password does not satisfy the current policy requirements时,要么把密码改复杂,要么临时调低校验等级:
SET GLOBAL validate_password.policy = LOW; SET GLOBAL validate_password.length = 6;需要说明的是,这是全局设置,测试环境可以用,生产环境不建议动。
7. 生产环境迁移的注意点:从 5.7 升到 8.x 时的 2059
MySQL 5.7 迁到 8.x 是很多团队都会经历的。数据迁移本身有官方工具可以操作,但这中间最容易忽略的就是认证插件差异,尤其在代码里、配置里、快捷连接工具里全部都要跟着适配。
7.1 迁移后 2059 为什么出现的概率极高
原因很简单,老库里的所有账号都是mysql_native_password,迁移到新库后,如果通过工具重建账号、导入用户表,新的 MySQL 8.x 默认插件会把新建的用户设置成caching_sha2_password。如果你的中间件、定时任务、BI 工具用的驱动还是老版本,连上就报 2059。
更麻烦的是,有些迁移工具只迁移数据不迁移用户,迁移完你需要手动重建所有业务账号,重建时没显式指定插件,默认就走新插件。
7.2 迁移时一次性把所有账号改成旧插件
你可以用 SQL 动态生成 ALTER 语句,一条条执行。比如在旧库导出用户清单,然后拼接语句:
SELECT CONCAT('ALTER USER ''', user, '''@''', host, ''' IDENTIFIED WITH mysql_native_password BY ''密码'';') FROM mysql.user WHERE plugin = 'caching_sha2_password' AND user NOT IN ('mysql.sys', 'mysql.session', 'mysql.infoschema');把输出结果复制到新库执行。注意密码部分需要手动补全,这个 SQL 只是方便你快速生成语句。改完后不需要重启 MySQL。
7.3 应用连接串里的参数适配
如果你的应用代码用 JDBC 连接 MySQL 8.x,且服务端用户还是新插件,那么驱动版本也很关键。使用老版本驱动(比如 mysql-connector-java 5.1.x)连接时,即使服务端没有强制要求新插件,也可能出现类似报错。升级驱动到 8.0.x 之后,连接串可以加上:
jdbc:mysql://localhost:3306/db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/ShanghaiallowPublicKeyRetrieval=true这个参数只有在连接方式不支持 SSL 时才需要。如果你的服务器开启了 SSL,那么这一行不写也没问题。
7.4 迁移后验证清单
迁移完别急着收工,我习惯按这个清单过一遍:
- 用命令行本地登录,确认账号密码可用;
- 用 Navicat 分别连接 localhost 和远程地址;
- 用应用代码跑一遍核心接口;
- 检查慢查询日志里有没有认证失败的记录。
8. Docker 部署 MySQL 8.x 时 2059 的处理细节
Docker 跑 MySQL 是另一类高频场景,处理思路略有不同,因为容器环境里改配置、改用户的路子都受镜像机制影响。
8.1 容器内的快速解决方式
先用 docker exec 进入容器:
docker exec -it mysql8 mysql -uroot -p然后执行:
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '你的密码'; FLUSH PRIVILEGES;这个方案和前面方案一相同,但注意容器重启后 MySQL 服务会重新启动,用户表是持久化在数据卷里的,所以改一次就长期有效,不需要每次都改。
8.2 通过环境变量直接创建旧插件用户(最省事)
如果你还没创建容器,可以在docker run时挂一个初始化脚本:
docker run -d \ --name mysql8 \ -e MYSQL_ROOT_PASSWORD=rootpass \ -v /opt/mysql-init:/docker-entrypoint-initdb.d \ -p 3306:3306 \ mysql:8在/opt/mysql-init/init.sql里写入:
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'rootpass'; FLUSH PRIVILEGES;容器首次初始化的时候会自动执行这个脚本,之后启动就默认带旧插件。
8.3 容器场景下升级客户端反而是更干净的路
有的人图省事,直接改容器内 MySQL 配置,挂载自定义 my.cnf 把默认插件设为旧插件,这确实能解决,但容器重建时你很容易忘了这回事,而且这个配置本身就是逆着官方默认方向走的。毕竟是本地开发环境,怎么方便怎么来,但如果你这个容器是为了模拟生产环境,那我不建议你改配置,保持默认新插件,然后用新版 Navicat 连接,才是最贴合真实线上环境的做法。
9. 我的选择建议与踩坑总结
9.1 三种方案怎么选
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 本地开发、随便连连 | 方案一:改用户插件 | 最快,影响面最小 |
| 正式环境、长期使用 | 方案二:升级 Navicat | 服务端保持强认证,客户端适配新标准 |
| 测试环境新建用户量大 | 方案三:改全局默认插件 | 省得每个用户单独改 |
| Docker 一次性起服务 | 方案一或初始化脚本 | 起完就能连,脚本可复用 |
| 生产迁移 5.7 -> 8.x | 方案二 + 升级驱动 | 保证线上安全等级不下滑 |
9.2 我踩过的三个真实坑,写出来给大家避开
第一个坑:只改了 localhost 的 root,忘了 Navicat 用的是远程地址连接。Navicat 连接配置里如果主机填的是127.0.0.1或局域网 IP,MySQL 匹配的是'root'@'%'或'root'@'127.0.0.1'而不是'root'@'localhost'。结果就是明明执行了 ALTER,Navicat 还是报 2059,瞎折腾半小时。
第二个坑:密码改完后插件又变回去了。我之前给测试环境改过 root 用户插件,后来同事重置了一次密码,用的语句没带插件名,结果 MySQL 8.0 默认又把插件切回caching_sha2_password,Navicat 再次报 2059。所以团队里如果有人会操作数据库,最好在文档里注明“改密必须带 WITH mysql_native_password”。
第三个坑:升级 Navicat 后连接串里的端口问题。有次升级完,旧版本里保存的连接默认走 3306 没问题,但新版本里如果之前选过 SSH 隧道,升级后部分配置会重置。连接超时的时候别急着怀疑服务端,重新检查一下连接配置的端口和 SSH 开关。
9.3 日常连接 MySQL 8.x 的预防思路
与其每次被 2059 搞一次,不如提前做点预防工作:
- 统一客户端版本:团队内部约定 Navicat 最低版本,避免每个人报错原因各不相同。
- 建用户时就确定插件:创建账号时顺手把
WITH mysql_native_password写进去,后续省事。 - 配置管理文档化:任何 ALTER USER、CREATE USER 操作都留痕,尤其是插件相关的变更。
- 定期检查用户插件矩阵:每月执行一次上面提到的
SELECT user, host, plugin FROM mysql.user,发现意外的旧插件用户及时评估切换。
我个人的体会是:这个错误本身不复杂,但因为它牵涉到客户端、服务端、账号权限、配置持久化几个层面,排查链路长,最容易在细节上翻车。只要把插件原理和修改路径搞清楚,它也就是顺手一条 SQL 的事。