MySQL 客户端连接数据库时,如果出现 Public Key Retrieval is not allowed,通常并不是 MySQL 服务本身停止或账号密码错误,而是客户端驱动在使用较新的认证方式时,对 RSA 公钥获取行为进行了限制。
这个问题在 MySQL 8.x、Java 应用、Spring Boot、JDBC、Navicat 等连接场景中比较常见。尤其是数据库账号使用 caching_sha2_password 认证插件,而连接没有启用 SSL 时,客户端可能无法直接完成密码认证,从而抛出类似下面的异常:
java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
一、为什么会出现 Public Key Retrieval is not allowed
MySQL 8.0 默认认证插件从传统的 mysql_native_password 转向了 caching_sha2_password。新的认证机制在特定连接条件下需要使用 RSA 公钥对密码进行加密传输。
简单来说,连接过程可能类似于:
Java程序 ↓ JDBC驱动 ↓ MySQL服务器 ↓ caching_sha2_password认证 ↓ 需要RSA公钥完成密码交换
如果当前连接没有使用 SSL,同时 JDBC 驱动又没有被允许从 MySQL 服务器获取 RSA 公钥,就可能出现:
Public Key Retrieval is not allowed
因此,这个异常的核心并不是“公钥不存在”,而是客户端默认不允许自动获取公钥。
二、最常见的解决方法:增加 allowPublicKeyRetrieval=true
对于 Java JDBC 连接,这是最直接、也是开发环境中最常见的处理方式。
原来的连接字符串可能是:
jdbc:mysql://localhost:3306/test
修改为:
jdbc:mysql://localhost:3306/test?allowPublicKeyRetrieval=true&useSSL=false
如果已经存在其他参数,则使用 & 继续追加:
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true
其中:
allowPublicKeyRetrieval=true
表示允许 JDBC 驱动从服务器获取 RSA 公钥。
而:
useSSL=false
表示关闭 SSL 连接。
两者经常一起出现,是因为很多本地开发环境并没有配置 SSL,而 caching_sha2_password 在非 SSL 场景下可能需要 RSA 公钥进行认证。
三、Spring Boot项目怎么修改
如果项目使用 Spring Boot,可以直接修改 application.properties。
例如:
propertiesspring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=123456
如果使用 YAML 配置:
YAMLspring: datasource: url: jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai username: root password: 123456
修改完成后重新启动项目即可。
如果项目使用 HikariCP、Druid 等连接池,通常不需要单独修改连接池配置,只要 JDBC URL 最终能够传递给 MySQL 驱动即可。
四、注意 JDBC URL 中的参数连接符
这个问题在实际配置中非常容易出错。
如果 URL 原本没有任何参数:
jdbc:mysql://127.0.0.1:3306/test
第一个参数使用:
?
例如:
jdbc:mysql://127.0.0.1:3306/test?allowPublicKeyRetrieval=true
如果已经存在参数:
jdbc:mysql://127.0.0.1:3306/test?useSSL=false
新增参数时应该使用:
&
正确写法:
jdbc:mysql://127.0.0.1:3306/test?useSSL=false&allowPublicKeyRetrieval=true
不要写成:
jdbc:mysql://127.0.0.1:3306/test?useSSL=false?allowPublicKeyRetrieval=true
因为 URL 查询参数中不能连续使用两个 ?。
五、MySQL Connector/J版本也可能影响问题
除了连接参数之外,还需要关注 MySQL JDBC 驱动版本。
例如 Maven 项目中常见的依赖:
XMLcom.mysql mysql-connector-j 8.4.0
较老版本可能使用:
XMLmysql mysql-connector-java 8.0.33
如果项目长期没有维护,建议检查实际加载的 Connector/J 版本。
可以通过 Maven 查看:
Bashmvn dependency:tree
重点确认项目中是否存在多个 MySQL 驱动版本。
例如:
mysql-connector-j:8.x.x
如果同时存在多个不同版本,可能导致运行时实际使用的驱动与预期不一致。
六、如果使用SSL,更推荐使用安全连接
allowPublicKeyRetrieval=true&useSSL=false 虽然能够解决很多本地开发环境中的连接问题,但并不意味着生产环境应该永久关闭 SSL。
生产环境更推荐建立加密连接,例如:
jdbc:mysql://db.example.com:3306/test?useSSL=true
并根据实际环境配置服务器证书、客户端信任库等参数。
这样密码和数据库通信内容能够通过 TLS 加密,而不是依赖非 SSL 环境下的 RSA 密钥交换。
因此可以简单理解为:
| 场景 | 建议 |
|---|---|
| 本地开发 | allowPublicKeyRetrieval=true&useSSL=false |
| 测试环境 | 根据安全要求决定 |
| 生产环境 | 优先配置 SSL/TLS |
| 高安全要求环境 | 不建议简单关闭 SSL |
七、另一种方案:修改MySQL用户认证插件
如果不希望客户端通过 allowPublicKeyRetrieval=true 获取公钥,也可以从 MySQL 用户认证方式入手。
首先查看当前用户使用的认证插件:
SQLSELECT user, host, plugin FROM mysql.user;
可能看到:
root localhost caching_sha2_password
这说明当前用户使用的是 MySQL 8 默认的 caching_sha2_password。
在某些兼容性要求较高的旧系统中,可以将用户修改为:
mysql_native_password
例如:
SQLALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY '123456';
然后刷新并重新建立连接。
不过,这种方式并不是解决 Public Key Retrieval is not allowed 的首选方案。
原因在于 caching_sha2_password 是 MySQL 8 推荐的现代认证机制。如果客户端本身支持该认证插件,没有必要为了规避一个连接参数而降低认证方式的现代化程度。
因此,更合理的处理顺序通常是:
检查JDBC驱动 ↓ 确认认证插件 ↓ 检查SSL配置 ↓ 开发环境增加allowPublicKeyRetrieval ↓ 生产环境优先配置SSL
八、使用MySQL命令行客户端时怎么办
需要注意,Public Key Retrieval is not allowed 并不一定只意味着 MySQL 服务存在故障。
如果使用的是 Java JDBC,重点应该检查 JDBC URL。
例如:
JavaString url = "jdbc:mysql://localhost:3306/test" + "?useSSL=false" + "&allowPublicKeyRetrieval=true"; Connection connection = DriverManager.getConnection(url, "root", "123456");
这里真正发挥作用的是:
allowPublicKeyRetrieval=true
如果使用的是其他数据库管理工具,则应根据该工具所使用的 MySQL 驱动配置相应的公钥获取选项,而不是简单照搬 JDBC URL。
九、Docker环境中的常见情况
Docker 部署 MySQL 后,这个问题也比较常见。
例如应用连接:
jdbc:mysql://mysql:3306/test?useSSL=false&allowPublicKeyRetrieval=true
这里的 mysql 是 Docker Compose 中 MySQL 服务的服务名。
例如:
YAMLservices: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: 123456 MYSQL_DATABASE: test app: image: my-java-app
应用容器内部不能简单使用:
localhost
因为对于 app 容器而言,localhost 指向的是应用容器自身。
应该根据 Docker 网络配置使用 MySQL 服务名:
jdbc:mysql://mysql:3306/test?useSSL=false&allowPublicKeyRetrieval=true
因此,如果修改 allowPublicKeyRetrieval=true 后仍然无法连接,还应该检查:
-
MySQL 容器是否正常运行;
-
数据库端口是否正确;
-
Docker 网络是否连通;
-
数据库用户名和密码是否正确;
-
MySQL 用户允许从当前主机连接;
-
JDBC 驱动版本是否匹配。
十、不要把这个错误误判成密码错误
很多开发人员看到数据库连接异常后,会第一时间修改密码。
但:
Public Key Retrieval is not allowed
与:
Access denied for user
并不是同一个问题。
例如密码错误更常见的是:
Access denied for user 'root'@'localhost'
而公钥获取问题通常表现为:
Public Key Retrieval is not allowed
两者的排查方向不同。
可以按照下面的方式快速判断:
| 错误信息 | 常见原因 |
|---|---|
| Public Key Retrieval is not allowed | 公钥获取策略或认证方式问题 |
| Access denied for user | 用户名、密码或权限问题 |
| Communications link failure | 网络、端口或MySQL服务问题 |
| Unknown database | 数据库名称不存在 |
| Connection refused | 目标端口没有正常监听 |
| No suitable driver | JDBC驱动缺失或配置错误 |
这样可以避免遇到连接异常后盲目修改数据库账号。
十一、推荐的JDBC连接配置
对于 MySQL 8.x + Java + JDBC 的本地开发项目,可以采用类似配置:
propertiesspring.datasource.url=jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=你的密码
如果使用 Java 原生 JDBC:
JavaString url = "jdbc:mysql://127.0.0.1:3306/test" + "?useUnicode=true" + "&characterEncoding=UTF-8" + "&useSSL=false" + "&allowPublicKeyRetrieval=true" + "&serverTimezone=Asia/Shanghai"; Connection conn = DriverManager.getConnection(url, "root", "你的密码");
其中几个参数各自承担不同作用:
useUnicode=true
用于启用 Unicode 相关处理。
characterEncoding=UTF-8
用于指定字符编码。
useSSL=false
表示不使用 SSL。
allowPublicKeyRetrieval=true
允许驱动获取服务器 RSA 公钥。
serverTimezone=Asia/Shanghai
用于明确连接的服务器时区,避免部分项目出现时区相关警告或异常。
这些参数并非全部针对 Public Key Retrieval 错误,实际项目应该根据数据库、驱动版本和安全要求进行配置。
十二、排查问题的正确顺序
遇到这个错误时,不建议一上来就修改 MySQL 用户认证方式。
可以按照以下顺序排查:
1. 确认MySQL服务正常
尝试使用 MySQL 客户端直接连接:
Bashmysql -h 127.0.0.1 -P 3306 -u root -p
如果这里都无法连接,就需要优先解决 MySQL 服务、端口或账号问题。
2. 检查JDBC驱动
确认项目中存在正确的 MySQL Connector/J。
Maven项目可以执行:
Bashmvn dependency:tree
检查是否存在重复或冲突版本。
3. 检查用户认证插件
执行:
SQLSELECT user, host, plugin FROM mysql.user;
重点查看目标账号是不是:
caching_sha2_password
4. 检查JDBC URL
确认是否包含:
allowPublicKeyRetrieval=true
本地开发环境还可以检查:
useSSL=false
5. 生产环境检查SSL
如果生产系统使用数据库加密连接,则不要为了快速解决异常而直接关闭 SSL。
6. 最后再考虑修改认证插件
只有在旧应用、旧驱动存在明确兼容性问题时,才考虑调整 MySQL 用户的认证方式。
十三、修改后仍然报错怎么办
如果已经添加:
allowPublicKeyRetrieval=true
但问题没有解决,可以重点检查以下几个方面。
第一,确认参数真的传递到了驱动。
有些项目会在配置文件中定义多个数据源,修改了一个数据源的 URL,却实际使用的是另一个数据源。
第二,检查连接池配置。
如果使用多个数据源,需要分别确认每个数据源的 JDBC URL。
第三,确认驱动版本。
旧版 Connector/J、项目依赖冲突以及驱动类配置错误,都可能造成连接行为与预期不同。
第四,确认SSL参数。
不要简单认为 useSSL=false 可以解决所有连接问题。如果服务器要求 SSL,那么客户端必须按照服务器的安全策略建立加密连接。
第五,检查MySQL账号的host。
例如:
SQLSELECT user, host, plugin FROM mysql.user WHERE user = 'root';
可能同时存在:
root@localhost root@%
实际连接来源不同,匹配到的账号也可能不同。
十四、这个错误的核心解决方案
如果你的场景是MySQL 8.x + Java JDBC + 本地开发环境,最直接的解决方案通常是在 JDBC URL 中添加:
allowPublicKeyRetrieval=true
例如:
jdbc:mysql://localhost:3306/test?allowPublicKeyRetrieval=true&useSSL=false
如果是 Spring Boot:
YAMLspring: datasource: url: jdbc:mysql://localhost:3306/test?allowPublicKeyRetrieval=true&useSSL=false username: root password: 你的密码
如果是生产环境,则不要把“关闭 SSL”当成通用方案。更合理的做法是正确配置 TLS,让数据库连接在加密通道中完成认证。
从根本上看,Public Key Retrieval is not allowed 是 MySQL 8 认证机制、JDBC 驱动安全策略以及连接加密方式之间的兼容问题。理解这一点后,排查时就不必反复修改用户名、密码或者重装 MySQL,而是可以从驱动、认证插件、SSL 和 JDBC URL 四个方向快速定位。