问题描述

我遇到的问题,可能之前也有人踩过这个坑,晚上加班coding,Java 连接 MySQL8.0 服务会间歇报错,错误信息如下:

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

解决方法:指定连接服务器的 RSA 公钥,或者允许客户端从服务器获取公钥

这边就要看是否用了 sha256_password 认证,大家都知道,TLS 协议保护是在密码传输过程中使用的,而且是必须的 ,但有时会出现不可用的 RSA 公钥,那换成服务器提供的公钥也可以。

我们可以通过 ServerRSAPublicKeyFile 指定连接服务器的 RSA 公钥。

也可以通过设置 AllowPublicKeyRetrieval = true 参数来允许客户端从服务器获取公钥。该参数默认是关闭的,因为如果打开它,一些恶意代理可能会通过中间人攻击(MITM)从而拿到明文密码。

具体介绍可以参阅英文文档:

other options

我给 JDBC 添加 AllowPublicKeyRetrieval = true 完参数问题确实有解决。

不过上面我说 Java 连接 MySQL 8.0 时出错是偶尔出现的,经过观察,我发现在三种情况下会出现,满足其中一点就会报错:

  1. 重启过MySQL Server后;
  2. 切换过MySQL MGR后;
  3. MySQL Server 做过变更后;

这样一看,上面的解决方法并不能完全说明问题,为什么是偶尔会出现问题?

好了,不卖关子了,答案揭晓。

caching_sha2_password 用户认证信息缓存

不知您是否留意,MySQL8.0 使用的默认的加密密码插件是什么?

就是 caching_sha2_password,它是通过 sha256 算法来加密密码。

此插件为了使已连接的用户的身份验证速度更快,会在内存中缓存 MySQL Server 用户的认证信息。

具体参阅下图的英文介绍:

caching_sha2_password

由于英文水平有限,加上这个英文介绍也不详细,我按照自己的意思来理解,大致意思是:

当 Java 程序通过 JDBC 驱动连接 MySQL 时,如果其它客户端之前已经登陆过该用户,那是会把该用户的验证信息缓存下来,此时即使Java客户端没有配置 RSA 公钥也可连接成功。

但是当MySQL服务被重启或配置修改后,用户认证缓存就被清掉了,所以导致”偶尔“报错的现象出现。

当连接之前没有认证缓存,或缓存被清掉后,这时再不指定 RSA 公钥,就会报错:Public Key Retrieval is not allowed

不过以上只是我的猜测,我想验证下我的想法,接下来搞一个测试。

测试过程

测试的main方法:

main方法

先再没有缓存的情况下,直接运行后,复现了我之前遇到的报错:

再用 mysql 客户端,用目标用户 user1 连接 MySQL Server,成功后就会有 user1 的认证缓存了:

接着再运行 java 测试方法,这回不报错了:

再次运行测试方法

再试试重启 MySQL 数据库后再跑测试程序,又触发报错:

重启MySQL

再次手工连接,虽然程序恢复正常:

清空用户缓存 flush privileges;,再运行程序又报错:

切换MGR的场景也验证了缓存的作用,之前新的 Primary 节点一直没有应用用户连接过,也就一直没有过缓存,所以当应用程序连接新的 Primary 节点时会报错。

结论

和我的猜测一致,因为插件 caching_sha2_password的存在导致用户连接MySQL服务后会缓存认证信息,只要同一用户的认证信息缓存存在,Java连接MySQL8.0时设不设置 RSA 公钥就看不出区别,但是MySQL重启、切换MGR等会清空缓存,所以这时没设置 RSA 公钥就会出现异常:Public Key Retrieval is not allowed

解决方案

择一即可:

  1. 指定 RSA 公钥;
  2. 设置参数 AllowPublicKeyRetrieval=True;
  3. 修改 MySQL Server 用户的密码加密插件为 mysql_native_password;

本文由《MySql教程网》原创,转载请注明出处!https://mysql360.com