MySQL连接错误:Public Key Retrieval is not allowed的解决方案

0 次阅读

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

例如:

properties
spring.datasource.url=jdbc:mysql://localhost:3306/test?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=123456

如果使用 YAML 配置:

YAML
spring:
  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 项目中常见的依赖:

XML

    com.mysql
    mysql-connector-j
    8.4.0

较老版本可能使用:

XML

    mysql
    mysql-connector-java
    8.0.33

如果项目长期没有维护,建议检查实际加载的 Connector/J 版本。

可以通过 Maven 查看:

Bash
mvn 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 用户认证方式入手。

首先查看当前用户使用的认证插件:

SQL
SELECT user, host, plugin
FROM mysql.user;

可能看到:

root    localhost    caching_sha2_password

这说明当前用户使用的是 MySQL 8 默认的 caching_sha2_password

在某些兼容性要求较高的旧系统中,可以将用户修改为:

mysql_native_password

例如:

SQL
ALTER 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。

例如:

Java
String 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 服务的服务名。

例如:

YAML
services:
  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 driverJDBC驱动缺失或配置错误

这样可以避免遇到连接异常后盲目修改数据库账号。


十一、推荐的JDBC连接配置

对于 MySQL 8.x + Java + JDBC 的本地开发项目,可以采用类似配置:

properties
spring.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:

Java
String 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 客户端直接连接:

Bash
mysql -h 127.0.0.1 -P 3306 -u root -p

如果这里都无法连接,就需要优先解决 MySQL 服务、端口或账号问题。

2. 检查JDBC驱动

确认项目中存在正确的 MySQL Connector/J。

Maven项目可以执行:

Bash
mvn dependency:tree

检查是否存在重复或冲突版本。

3. 检查用户认证插件

执行:

SQL
SELECT 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。

例如:

SQL
SELECT 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:

YAML
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?allowPublicKeyRetrieval=true&useSSL=false
    username: root
    password: 你的密码

如果是生产环境,则不要把“关闭 SSL”当成通用方案。更合理的做法是正确配置 TLS,让数据库连接在加密通道中完成认证。

从根本上看,Public Key Retrieval is not allowedMySQL 8 认证机制、JDBC 驱动安全策略以及连接加密方式之间的兼容问题。理解这一点后,排查时就不必反复修改用户名、密码或者重装 MySQL,而是可以从驱动、认证插件、SSL 和 JDBC URL 四个方向快速定位。