☰
MySQL 8.0认证插件报错:Public Key Retrieval详解
2026/10/16 4:08:16 网站建设 项目流程

写代码这些年,跟数据库打的交道不算少。要说哪种报错最让人觉得莫名其妙,连接层的坑绝对排第一。比如今天要聊的这个——MySQL 连接时抛出 "Public Key Retrieval is not allowed"。光看这行英文,十个有九个会懵:什么叫"不允许获取公钥"?我连个数据库跟公钥有什么关系?前几天我搭新环境,数据库装了 MySQL 8.0,应用启动的一瞬间就栽在这行报错上,连源码带官方文档翻了一通,才把来龙去脉彻底搞清楚。这篇文章就把这个报错从头到尾讲透:它为什么出现、有哪些解决方案、每种方案在什么场景下用,以及我实际踩过的几个坑。

1. 这个报错到底在说什么(根因拆解)

1.1 先还原一下现场

先看这个报错最常见的出现场景。一个典型的 Java 项目,连接串大概是这样的:

jdbc:mysql://127.0.0.1:3306/myapp_db?useSSL=false

数据库是 MySQL 8.0,应用用的是 MySQL Connector/J 8.x。启动之后,抛出来的异常大致是这样:

java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed

注意,这个异常抛在连接建立阶段,你连查询都还没写就已经挂了。如果你用的是图形化工具,比如老版本 DBeaver 或者某些 IDE 自带的数据库面板,也会在测试连接的时候弹同样的错。换句话说,这个问题跟你的 SQL 没关系,跟表结构也没关系,纯粹是客户端和服务端在握手阶段谈崩了。

还有一类常见误判是把这当成网络故障或账号权限问题。很多人一开始会去查防火墙、查账号授权、甚至重装客户端,折腾一圈回来发现毫无用处。判断方向其实很简单:如果报错文案里带着 Public Key、RSA、Authentication plugin 这些字眼,基本可以锁定是认证流程的问题,而不是网络或权限的问题。

1.2 根因:MySQL 8.0 换了密码认证插件

要搞明白这个报错,得从 MySQL 的认证机制说起。MySQL 5.7 及更早版本,默认的认证插件是 mysql_native_password,客户端和服务端用一套基于 SHA1 的质询-响应流程校验密码。整个过程不需要额外的密钥交换,所以老项目很少遇到"公钥"这种概念。

MySQL 8.0 把默认认证插件换成了 caching_sha2_password。这个插件更安全,但也更复杂:第一次连接,或者服务端缓存失效之后,客户端需要做一次"全认证"。全认证的流程是,服务端下发一段随机数 scramble,客户端必须结合密码和这个随机数计算响应。关键在传输环节——为了避免密码在网络里以明文或弱加密形式传输,这个全认证过程需要一个安全通道。如果连接没走 SSL,客户端就必须用服务端的 RSA 公钥对密码做加密再发过去。于是,"获取服务端的 RSA 公钥"就成了绕不开的一步。

JDBC 驱动出于安全考虑,默认不允许客户端主动去获取这把公钥,必须由你在连接串里显式授权。你忘了授权,驱动就直接拒绝这个动作,然后抛出 "Public Key Retrieval is not allowed"。

1.3 "不允许获取公钥"是安全策略,不是 bug

看到这里有人会问:为什么驱动要默认禁止获取公钥?这不是给自己找麻烦吗?其实这是有道理的。如果连接走的是不加密通道,客户端向服务端请求公钥的这个过程本身是可以被中间人劫持的。攻击者可以在网络中间冒充服务器,把一份自己伪造的公钥递给客户端,客户端用这把公钥加密的密码,攻击者就能用自己的私钥解出来。所以驱动默认把这个能力关掉,宁可让连接失败,也不让用户在不知不觉中把密码暴露给不可信的对方。这不是 bug,是安全策略。

理解了这个背景,你就能明白,网上流传的各种"改用户名密码""重装驱动"之类的建议大多不靠谱,真正对症的解法其实就那么几类。下面把每一类都展开说清楚,你按场景选就行。

2. 最快的修复:JDBC连接串加一个参数

2.1 具体怎么改

如果你就是本地开发、内网测试,最直接的修复方式是在 JDBC 连接串里加一个参数 allowPublicKeyRetrieval=true:

jdbc:mysql://127.0.0.1:3306/myapp_db?useSSL=false&allowPublicKeyRetrieval=true

对于 Spring Boot 项目,通常是在 application.yml 或 application.properties 里的 datasource url 上追加:

spring: datasource: url: jdbc:mysql://127.0.0.1:3306/myapp_db?useSSL=false&allowPublicKeyRetrieval=true username: root password: your_password

改完重启应用,连接基本就恢复了。注意参数名不要写错,是 allowPublicKeyRetrieval。这个驼峰写法是 Connector/J 官方文档里的标准写法,别凭记忆改成别的风格。我见过有人写成 Java 类属性风格大写的 AllowPublicKeyRetrieval,还有人把 Python 驱动里的下划线写法 allow_public_key_retrieval 搬过来,结果都不生效,照样报错。如果你是在 Spring Boot 的 application.properties 里配置,也可以写成 spring.datasource.url=jdbc:mysql://... 整串一起写在等号后面,注意 & 符号不需要转义,直接写就行。

2.2 这个参数的安全边界

前面说了,这个参数默认是 false 是出于防中间人劫持的考虑。所以加这个参数之前,你先问自己一个问题:这个连接要穿过公网或不可信网络吗?如果答案是肯定的,不建议只靠这一个参数解决,请继续看后面的 SSL 方案。

更严谨的做法是:不走"获取公钥",而是把服务端的 RSA 公钥文件直接配置在客户端。Connector/J 提供 serverRSAPublicKeyFile 参数,先把服务端的公钥导出成 pem 文件,然后在连接串里指定:

jdbc:mysql://127.0.0.1:3306/myapp_db?useSSL=false&serverRSAPublicKeyFile=/path/to/public_key.pem

这样客户端不再需要主动去服务端拿公钥,而是用你预先放好的公钥去加密密码。既不受中间人劫持影响,密码也不会以明文形式出现在网络上。缺点是公钥文件要分发到所有连这个库的客户端机器上,换钥匙的时候还得同步更新,运维成本略高。适合那些对安全要求比较严格、一时半会儿又没法全面上 SSL 的场景。

2.3 其他客户端和语言驱动的对应配置

不只是 Java 的 Connector/J 会遇到这个报错,其他语言和工具也可能碰到同源问题,只是报错文案略有差异。常见的对应配置我整理成了表格:

客户端/驱动对应参数说明
MySQL Connector/J (JDBC)allowPublicKeyRetrieval=true连接串或连接属性里设置
mysql-connector-python (官方 Python 驱动)allow_public_key_retrieval=Trueconnect() 参数或配置字典里设置,参数名是下划线风格,和 JDBC 不一样
PyMySQL无需配置(新版自动处理)0.10.0 及以上版本支持 caching_sha2_password
Node.js mysql2无需配置(新版自动处理)默认支持 caching_sha2_password,别再只用老 mysql 包
DBeaver驱动属性里勾选 allowPublicKeyRetrieval在连接编辑页的"驱动属性"标签页找
MySQL Workbench无需配置官方工具已处理

看到这里你应该也发现了,这个问题跟"MySQL 8.0 + 新认证插件 + 非 SSL 连接"这个组合强相关。如果选了 SSL,很多环节可以绕开公钥检索;如果用了支持新认证插件的较新驱动,驱动内部也能更优雅地处理。接下来详细说 SSL 这条路。

3. 更稳妥的方案:走 SSL 加密通道

3.1 开启 SSL 的具体配置

如果你对安全比较敏感,或者公司安全规范要求所有数据库连接必须加密,正路是开 SSL。MySQL 8.0 其实内置了自动生成的 SSL 证书,很多情况下你不需要额外配置就能直接启用。

JDBC 连接串可以这样改:

jdbc:mysql://127.0.0.1:3306/myapp_db?sslMode=REQUIRED&allowPublicKeyRetrieval=true

注意这里我把 allowPublicKeyRetrieval 也加上了。为什么?因为即使走了 SSL,Connector/J 在部分版本和部分场景下握手时仍然会检查相关配置,与其在"改了 SSL 还继续报公钥检索"的边界情况里反复折腾,不如一组参数把两种认证路径都覆盖到。sslMode=REQUIRED 表示强制使用 SSL,但不校验服务端证书的合法性,适合测试环境。生产环境建议用 VERIFY_CA 或 VERIFY_IDENTITY:前者校验服务端证书是否由可信 CA 签发,后者还会进一步校验主机名。用这两个模式时,你需要提前把 CA 证书导入客户端的 truststore,否则会报证书校验失败。

3.2 SSL 和 allowPublicKeyRetrieval 怎么取舍

简单粗暴地总结一下各场景的推荐方案:

场景推荐方案理由
本地开发、内网联调allowPublicKeyRetrieval=true配置最省事,性能损耗可忽略
生产环境,允许加密通道sslMode=REQUIRED + allowPublicKeyRetrieval=true兼顾安全与兼容性;有条件再升级到 VERIFY_CA
生产环境,暂不能上 SSL 但要求密码不裸奔serverRSAPublicKeyFile 指定公钥文件避免公钥被劫持,但增加密钥分发成本
历史遗留系统,客户端无法升级改认证插件(慎用,往下看)过渡期手段,要记录改造点

这里有个常见误区:很多人以为开了 SSL 就万事大吉,结果只把 useSSL=true 加上,没管公钥的事情,仍然报错。其实从上面的全认证流程能看出来,SSL 和 RSA 公钥是两条并行的密码传输路径,某些握手状态下客户端可能先尝试公钥检索,如果被禁止就直接抛异常,根本走不到加密通道那一步。这也是我推荐"sslMode=REQUIRED 和 allowPublicKeyRetrieval=true 一起写"的原因——用最小的参数组合覆盖最多的握手场景。

4. 数据库侧调整:改用户认证插件

4.1 操作步骤

除了在客户端动手,还可以在数据库侧把用户的认证插件改回 mysql_native_password。这样客户端走的是老一套 SHA1 质询-响应流程,不需要 RSA 公钥,报错自然消失。操作很简单:

ALTER USER 'your_user'@'your_host' IDENTIFIED WITH mysql_native_password BY 'your_password'; FLUSH PRIVILEGES;

比如给本地的 root 用户改:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'MyNewPass123!';

改完再用 JDBC 裸连,不带 allowPublicKeyRetrieval,连接就正常了。要注意的是,改完认证插件之后设置密码,密码策略仍然受 validate_password 组件约束,太简单的密码可能不满足要求。如果当前账号在多个主机下有不同条目,记得逐个处理,比如 root 可能同时存在 'localhost' 和 '127.0.0.1' 两条记录,漏改其中一条,对应来源连接还是照样报错。

4.2 但请务必三思:MySQL 8.4 之后这条路在变窄

这个方案在 MySQL 8.0 早期版本很流行,很多 DBA 为了省事,把默认账号全都改回 mysql_native_password。但我要提醒你,这条路正在被官方慢慢堵死。

MySQL 8.0.34 开始,mysql_native_password 插件被标记为废弃;MySQL 8.4 中该插件默认被禁用,你需要显式配置 mysql_native_password=ON 才能继续使用;而到了 MySQL 9.0,这个插件已经被移除了。也就是说,你现在用改认证插件的办法解决问题,等以后数据库再升级,同样的坑会原封不动地回来,甚至会更难处理。所以除非你的客户端驱动老到没法升级、业务代码冻结没法改连接串,否则我不建议把这个作为首选方案。

如果实在要用,也建议只给个别兼容性遗留账号改,而不是一刀切把全局默认认证插件改成 mysql_native_password;同时记录好这些账号,把它们当成后续升级时的技术改造点,早做排期。

5. 图形化工具和驱动兼容性的那些坑

5.1 DBeaver、Navicat、Workbench 的表现

很多前端开发、数据分析同学不是用 Java,而是用 DBeaver、Navicat 这类图形化工具连 MySQL。遇到同样报错时,处理路径不太一样。

DBeaver 里,连接 MySQL 8.0 的时候如果弹了 Public Key Retrieval is not allowed,你可以在连接编辑器的"驱动属性"(Driver properties)标签页里找到 allowPublicKeyRetrieval,把它从 false 改成 true,然后重新测试连接。有些版本还能在 SSL 配置页里直接打开加密连接,效果一样。要注意 DBeaver 不同版本放的入口不一样,新版本通常直接能看到驱动属性列表,老版本可能在"编辑连接"的高级设置里藏着,找不到就两个地方都翻翻。

Navicat 新版一般能自动处理 caching_sha2_password 认证,我实测很少再碰到这个报错。如果用的版本比较老,优先升级客户端。MySQL Workbench 作为官方工具,对这种认证流程支持得最顺,基本不需要手动配置。图形化工具的核心原则是:先确保驱动够新,再谈参数配置。版本越老,你遇到这类问题的概率越高,跟数据库版本、工具版本之间的搭配关系很大。

5.2 旧驱动是重灾区

说到驱动版本,这其实是比参数配置更隐蔽的坑。很多项目报这条错误,不是因为没加参数,而是驱动的版本已经老到不认识 caching_sha2_password 了。比如早期的 Connector/J 5.x、非常老版本的 PyMySQL,连"取公钥"这个逻辑都没有,报错方式五花八门,有的报 "Authentication plugin 'caching_sha2_password' cannot be loaded",有的报 "RSA Encryption is required"。

碰到这种情况,别急着加参数,先看驱动版本。Connector/J 5.x 连 MySQL 8.0 本身就属于跨版本使用,官方不推荐,最好的办法是升级到 8.0.x 及以上。Python 这边的 PyMySQL 至少要 0.10.0 以上,官方 mysql-connector-python 同理,越新越稳。Node.js 建议使用 mysql2 而不是老掉牙的 mysql 包,后者对 MySQL 8 的新认证支持很弱,生产环境不建议再用。

判断是不是驱动老化的方法很简单:看报错堆栈里有没有"caching_sha2_password"或者"Authentication plugin"关键词。如果驱动连插件名都不知道,那加 allowPublicKeyRetrieval 参数也没有用,因为它根本不认识这个开关,任何配置都白搭。这种纯升级驱动就能解决的问题,别在配置上耗时间。

6. 常见问题与排查实录

6.1 报错变体速查表

这个问题的报错在不同驱动和不同版本下长得不完全一样,我把常见变体和对应处理列成一张表:

报错文案常见原因处理方向
Public Key Retrieval is not allowedConnector/J 默认禁止公钥检索allowPublicKeyRetrieval=true 或走 SSL
Authentication requires secure connection全认证需要安全通道,但连接未加密开 SSL,或提供 RSA 公钥获取方式
RSA Encryption is required客户端无法完成 RSA 加密(常见于老驱动)升级驱动,或临时改认证插件过渡
Authentication plugin 'caching_sha2_password' cannot be loaded驱动太老,不认识新认证插件升级驱动
Client does not support authentication protocol客户端工具/库不支持缓存 sha2 认证升级客户端库或图形化工具

注意,有些报错文案看着不一样,根子其实是同一个:客户端不知道该怎么安全地完成 caching_sha2_password 全认证。排查的时候不要光抄报错文案去搜,先确认三件事:MySQL 是什么版本、客户端工具或驱动是什么版本、连接是不是走了 SSL。这三件事确定了,问题范围基本就锁定了一半。

6.2 我踩过的几个坑

最后分享几个我实际踩过的坑,你要是遇到了能少走弯路。

第一个坑是参数名拼错。早期我记性不好,把 allowPublicKeyRetrieval 记成了各种变体,加在 JDBC 连接串里完全不生效,日志还是原样报错。后来养成习惯:凡是 JDBC 连接参数,先翻官方驱动属性列表,不要凭感觉写。这个参数在官方文档里列的位置不难找,是连接级属性,不是系统属性,别放错地方。

第二个坑是在服务端做过主从切换之后偶发报错。有一次项目里数据库账号被 DBA 重建过,默认还是 caching_sha2_password,应用侧连接串里其实已经加了 allowPublicKeyRetrieval=true,按理说可以连上,结果还是偶发报错。查了半天发现,服务端做过主从切换,新的主库上这个账号的认证插件配置跟旧库不一样,部分连接走了老路径就出问题。所以说,改了客户端参数还报错的话,记得去服务端核实账号当前的 authentication plugin 到底是不是 caching_sha2_password,别只在客户端折腾。

第三个坑跟 SSL 相关。有次我图省事,只把 useSSL=true 加上,以为加密就完事了,结果连接照样抛 "Public Key Retrieval is not allowed"。原因前面也说了,SSL 和公钥检索在连接握手的不同阶段出现,不是有你没我的关系。Connector/J 8.x 下最稳的一组参数是 sslMode=REQUIRED 加 allowPublicKeyRetrieval=true 一起上,别再只写一个了。

说到底,Public Key Retrieval is not allowed 就是一个"新认证机制 + 旧客户端行为"碰撞出来的连接层报错。从我自己的经验看,处理这类问题的顺序应该是:先确认版本组合(MySQL、驱动),再确认连接是否加密,最后决定加参数、上 SSL 还是改账号插件。我个人最常用的组合,内网开发和测试一律 allowPublicKeyRetrieval=true,生产环境则要求 sslMode=VERIFY_CA 加 allowPublicKeyRetrieval=true,基本没有例外。这套组合帮我省了无数次连接排障的时间,也希望你看完这篇文章之后,再遇到这个报错,能直接说出它的来龙去脉,而不是又一次打开搜索引擎。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询