1. 问题背景与现象分析
最近在MySQL 8.0+环境中使用JDBC连接时,不少开发者遇到了"Public Key Retrieval is not allowed"的错误提示。这个看似简单的连接错误背后,实际上涉及到MySQL 8.0引入的新安全机制。
当使用较新版本的MySQL Connector/J驱动(8.0+)连接MySQL 8.0+服务器时,如果客户端没有正确配置,就会抛出这个异常。错误信息通常会伴随以下内容:
code复制java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
这个问题的本质是MySQL 8.0默认使用了更安全的caching_sha2_password认证插件,而旧版的mysql_native_password插件已被逐渐淘汰。新插件在身份验证过程中需要交换RSA公钥,但客户端默认禁止自动获取这个公钥,因此导致了连接失败。
2. 解决方案全景图
针对这个连接问题,我们主要有三种解决路径,每种方案适用于不同的场景:
- 客户端配置方案:在JDBC连接字符串中添加参数(快速修复)
- 服务端用户方案:修改MySQL用户的认证方式(长期方案)
- 服务端配置方案:更改MySQL服务器的默认认证插件(系统级方案)
下面我将详细解析每种方案的适用场景、具体操作步骤以及背后的技术原理。
3. 方案一:JDBC客户端配置调整
3.1 连接参数配置
这是最快速的解决方案,特别适合以下场景:
- 你无法修改MySQL服务器配置
- 只是临时需要建立连接
- 开发测试环境使用
在JDBC连接URL中添加两个关键参数:
java复制String url = "jdbc:mysql://localhost:3306/mydb?allowPublicKeyRetrieval=true&useSSL=false";
参数说明:
allowPublicKeyRetrieval=true:允许客户端从服务器获取公钥useSSL=false:禁用SSL加密(仅限测试环境)
注意:在生产环境中,建议保持useSSL=true以确保传输安全。如果启用SSL,还需要配置信任证书等参数。
3.2 参数背后的安全考量
MySQL 8.0+默认使用caching_sha2_password插件,它比传统的mysql_native_password更安全,但需要RSA密钥对进行密码交换。当客户端无法访问服务器上的公钥文件时,就需要通过网络传输公钥,这存在潜在的安全风险。
因此MySQL Connector/J默认禁止自动获取公钥(allowPublicKeyRetrieval=false)。在开发环境可以放宽这个限制,但在生产环境应该考虑更安全的方案。
4. 方案二:修改用户认证方式
4.1 更改为传统认证插件
如果你有MySQL的管理权限,更根本的解决方案是将用户认证方式改为传统的mysql_native_password:
sql复制-- 登录MySQL服务器
mysql -u root -p
-- 修改指定用户的认证方式
ALTER USER 'username'@'host' IDENTIFIED WITH mysql_native_password BY 'password';
-- 刷新权限
FLUSH PRIVILEGES;
4.2 认证插件对比分析
| 特性 | caching_sha2_password | mysql_native_password |
|---|---|---|
| 安全性 | 更高(SHA-256 + RSA加密) | 较低(SHA1) |
| 性能 | 较高(支持缓存) | 一般 |
| 兼容性 | 仅MySQL 8.0+ | 所有版本 |
| 是否需要公钥交换 | 是 | 否 |
| 推荐使用场景 | 生产环境 | 遗留系统兼容 |
4.3 批量修改用户认证方式
如果需要修改多个用户,可以使用以下查询生成ALTER USER语句:
sql复制SELECT CONCAT('ALTER USER ''',user,'''@''',host,''' IDENTIFIED WITH mysql_native_password BY ''',
IFNULL(authentication_string,''),''';')
FROM mysql.user
WHERE plugin='caching_sha2_password';
执行生成的SQL语句即可批量修改认证方式。
5. 方案三:服务器配置修改
5.1 修改默认认证插件
对于MySQL 8.0+服务器,可以修改配置文件使所有新用户默认使用传统认证方式:
-
找到MySQL配置文件(通常位于):
- Linux:
/etc/mysql/my.cnf或/etc/my.cnf - Windows:
my.ini
- Linux:
-
在[mysqld]段落下添加:
ini复制default_authentication_plugin=mysql_native_password
- 重启MySQL服务:
bash复制sudo systemctl restart mysql # Linux系统
net stop mysql && net start mysql # Windows系统
5.2 配置前后的注意事项
- 影响范围:此修改只影响之后创建的新用户,已有用户保持不变
- 安全权衡:降低了安全性换取兼容性,需评估业务需求
- 版本兼容:MySQL 5.7及以下版本无需此配置
- 最佳实践:建议在测试环境验证后再应用到生产环境
6. 深入原理与技术细节
6.1 MySQL认证流程解析
当使用caching_sha2_password插件时,完整的认证流程如下:
- 客户端发起连接请求
- 服务器发送随机盐值(salt)
- 客户端计算密码哈希:SHA256(SHA256(password)+salt)
- 如果需要RSA加密(如SSL未启用),服务器会发送公钥
- 客户端使用公钥加密密码哈希并发送给服务器
- 服务器验证密码并建立连接
6.2 公钥交换的两种模式
-
文件模式:
- 服务器公钥存储在
sha256_password_public_key文件中 - 客户端通过本地文件获取公钥(更安全)
- 服务器公钥存储在
-
网络模式:
- 客户端从服务器请求公钥
- 需要设置allowPublicKeyRetrieval=true
- 存在中间人攻击风险
6.3 性能与安全最佳实践
对于生产环境,我推荐以下配置组合:
java复制String url = "jdbc:mysql://prod-db:3306/mydb?"
+ "useSSL=true&"
+ "requireSSL=true&"
+ "verifyServerCertificate=true&"
+ "allowPublicKeyRetrieval=false&"
+ "serverRSAPublicKeyFile=/path/to/public_key.pem";
这种配置既保证了安全性(强制SSL+证书验证),又避免了网络公钥传输。
7. 常见问题排查指南
7.1 问题现象与解决方案速查表
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Public Key Retrieval错误 | 未允许公钥获取 | 添加allowPublicKeyRetrieval=true |
| 连接缓慢 | RSA密钥生成中 | 预生成RSA密钥对 |
| SSL相关错误 | 证书配置问题 | 检查useSSL/trustCertificate参数 |
| 认证方式不支持 | 驱动版本过旧 | 升级Connector/J到8.0+版本 |
| 修改认证方式无效 | 权限未刷新 | 执行FLUSH PRIVILEGES |
7.2 典型错误案例
案例1:开发环境可以连接,但生产环境失败
- 原因:生产环境禁用了allowPublicKeyRetrieval
- 解决:在生产环境配置serverRSAPublicKeyFile参数
案例2:修改认证方式后仍然报错
- 原因:连接池缓存了旧连接
- 解决:重启应用或清空连接池
案例3:部分客户端能连,部分不能
- 原因:客户端驱动版本不一致
- 解决:统一升级到最新版Connector/J
8. 版本兼容性指南
8.1 MySQL各版本认证方式变化
| MySQL版本 | 默认认证插件 | 主要变化 |
|---|---|---|
| 5.6及以下 | mysql_native_password | 传统SHA1认证 |
| 5.7 | mysql_native_password | 开始支持caching_sha2_password |
| 8.0+ | caching_sha2_password | 默认使用新插件,安全性更高 |
8.2 Connector/J驱动兼容性
- 5.1.x:仅支持传统认证
- 8.0.x:完整支持新认证方式
- 建议:MySQL 8.0+服务器应使用Connector/J 8.0+
在实际项目中,我遇到过因驱动版本不匹配导致的认证问题。例如使用Connector/J 5.1连接MySQL 8.0时,即使配置正确也会失败。这时要么降级MySQL认证方式,要么升级驱动版本。
9. 生产环境部署建议
经过多个项目的实践验证,我总结出以下部署方案:
-
新项目:
- 使用MySQL 8.0+
- 保持caching_sha2_password默认设置
- 在客户端配置SSL+公钥文件
- 使用最新版Connector/J驱动
-
遗留系统迁移:
- 先修改认证方式保证兼容
- 逐步升级客户端驱动
- 最后考虑恢复新认证方式
-
混合环境:
- 创建两种认证方式的用户
- 新应用使用新认证
- 旧系统使用传统认证
一个实际项目中的经验:在金融系统中,我们最终采用了折中方案 - 在应用服务器上预置MySQL公钥文件,既保证了安全性,又避免了allowPublicKeyRetrieval的风险。具体配置如下:
java复制// 生产环境推荐配置
String url = "jdbc:mysql://finance-db:3306/core?"
+ "useSSL=true&"
+ "requireSSL=true&"
+ "serverRSAPublicKeyFile=/etc/mysql/public_key.pem";
这种配置方式经过了安全团队的审计认可,运行三年未出现任何安全问题。