MySQL JDBC 报 Public Key Retrieval is not allowed:检查 TLS 和 RSA 认证
Java 连接 MySQL 8 后出现公钥获取限制,区分 TLS、固定 RSA 公钥和临时获取,保留证书校验及驱动版本条件。
Java 应用连接 MySQL 时抛出 Public Key Retrieval is not allowed,尤其是升级到 MySQL 8、关闭 JDBC 的 SSL,或数据库重启后首次连接时出现,先检查认证传输方式。它通常涉及 caching_sha2_password 或 sha256_password 的完整认证阶段:连接没有使用 TLS,驱动又没有可信的服务端 RSA 公钥,且禁止临时索取公钥。
本文针对 MySQL 协议的 Connector/J。下面的 sslMode 配置要求 Connector/J 8.0.13 及以上;旧驱动应先核对与服务器、JDK 的兼容范围,再升级到受支持的版本。这个错误不能证明密码正确,也不能证明所有同名账号都使用同一种认证插件。
查清账号插件和应用真正使用的参数
让数据库管理员查看应用账号对应的记录。以下是只读查询示例,app_user 替换成实际用户名:
SELECT User, Host, plugin
FROM mysql.user
WHERE User = 'app_user';
同名账号可能有不同的 Host 和插件。不要只检查 root@localhost,应用从其他机器或容器连接时,匹配的账号未必是这一条。普通业务账号通常没有读取 mysql.user 的权限,不应为此扩大应用权限。
同时检查最终生效的 JDBC 属性,特别是 useSSL=false、sslMode=DISABLED、serverRSAPublicKeyFile 和 allowPublicKeyRetrieval。Spring 配置、环境变量、连接池和 DBeaver 驱动属性可能各有一份设置。检查时隐藏密码,不把整个带凭据的连接串写进日志。
sslMode=PREFERRED 允许在服务器不支持 TLS 时退回明文连接;它不等于强制 TLS。显式设置 sslMode 后,相关旧 SSL 属性会被忽略,混用两套参数容易误判。
生产连接优先使用校验证书的 TLS
服务器已启用 TLS、证书主机名正确,且 Java 信任库信任签发 CA 时,可以使用如下 JDBC URL 示例:
jdbc:mysql://db.example.net:3306/appdb?sslMode=VERIFY_IDENTITY&allowPublicKeyRetrieval=false
db.example.net、端口和库名都是占位值。VERIFY_IDENTITY 要求加密连接,并校验 CA 与连接主机名。私有 CA 需要配置应用专用信任库,或确认现有 JVM 信任库包含该 CA;只修改 URL 不会自动解决信任问题。
若错误变成证书链不受信任或主机名不匹配,说明排查已经进入 TLS 层。修正信任库、证书或连接域名,保留校验。REQUIRED 只强制加密,不提供同样的身份校验;直接关闭 SSL 会重新回到公钥交换问题。
这项修改只改变客户端连接方式。上线前保存原连接配置,确认所有应用实例都能读取信任库,逐步重建连接池。若新连接全部失败,恢复原配置可以恢复变更前状态,但仍需单独修复原有的认证问题。
无法使用 TLS 时,区分固定公钥和临时获取
在明确允许非 TLS 传输的环境中,可由管理员通过可信渠道提供服务器 RSA 公钥,再配置驱动读取该文件。示例:
jdbc:mysql://db.example.net:3306/appdb?sslMode=DISABLED&serverRSAPublicKeyFile=/etc/app/mysql-public-key.pem&allowPublicKeyRetrieval=false
这必须是对应认证插件实际使用的 RSA 公钥,不是服务端私钥,也不是任意 TLS 证书。路径相对于应用运行环境,容器内必须能读取对应文件。服务器轮换 RSA 密钥后,应同步更新客户端;错误公钥可能导致认证失败。
RSA 在这里保护的是密码交换,后续 SQL 与结果仍通过非 TLS 连接传输。因此,固定公钥不能替代传输加密。
allowPublicKeyRetrieval=true 允许驱动在握手中向服务端索取公钥,可以消除这一限制,却没有独立确认公钥来源。只在隔离的本地开发环境、链路可信且明确接受风险时考虑使用;不要把它与 useSSL=false 一起复制成生产默认配置。退出临时调试后恢复该参数为 false,并重建连接池。
昨天能连,重启后失败,也符合这个场景
caching_sha2_password 有认证缓存。命中缓存的后续连接与缓存未命中时的完整认证要求不同;服务器重启、密码变更或清理权限缓存后,可能重新需要 TLS 或 RSA 密码交换。
因此,偶尔成功一次不能证明连接配置完整。也不需要为了验证猜测而在生产执行 FLUSH PRIVILEGES,它会影响使用该插件的其他账号缓存。
不要通过切换到 mysql_native_password 作为通用修复。这个插件在 MySQL 8.4 默认禁用,MySQL 9.0 起移除,旧教程中的切换命令存在明确版本边界。
用新建连接确认修复结果
修复后应关闭并重建受影响的连接池,确认新连接能够认证,而不是只复用旧连接。使用 TLS 路径时,可在同一个业务连接中执行以下只读查询:
SHOW SESSION STATUS LIKE 'Ssl_cipher';
SELECT CURRENT_USER(), DATABASE();
Ssl_cipher 非空说明该会话使用了 TLS;主机名与 CA 校验是否启用仍需核对驱动配置。CURRENT_USER() 用于确认实际匹配的账号,DATABASE() 用于确认目标库。新连接稳定成功、匹配正确账号,且保留预定的证书校验,才算完成这一问题的处理。


