问题描述
我遇到的问题,可能之前也有人踩过这个坑,晚上加班coding,Java 连接 MySQL8.0 服务会间歇报错,错误信息如下:
java.sql.SQLNonTransientConnectionException: Public Key Retrieval is not allowed
解决方法:指定连接服务器的 RSA 公钥,或者允许客户端从服务器获取公钥
这边就要看是否用了 sha256_password
认证,大家都知道,TLS 协议保护是在密码传输过程中使用的,而且是必须的 ,但有时会出现不可用的 RSA 公钥,那换成服务器提供的公钥也可以。
我们可以通过 ServerRSAPublicKeyFile
指定连接服务器的 RSA 公钥。
也可以通过设置 AllowPublicKeyRetrieval = true
参数来允许客户端从服务器获取公钥。该参数默认是关闭的,因为如果打开它,一些恶意代理可能会通过中间人攻击(MITM)从而拿到明文密码。
具体介绍可以参阅英文文档:
我给 JDBC 添加 AllowPublicKeyRetrieval = true
完参数问题确实有解决。
不过上面我说 Java 连接 MySQL 8.0 时出错是偶尔出现的,经过观察,我发现在三种情况下会出现,满足其中一点就会报错:
- 重启过MySQL Server后;
- 切换过MySQL MGR后;
- MySQL Server 做过变更后;
这样一看,上面的解决方法并不能完全说明问题,为什么是偶尔会出现问题?
好了,不卖关子了,答案揭晓。
caching_sha2_password 用户认证信息缓存
不知您是否留意,MySQL8.0 使用的默认的加密密码插件是什么?
就是 caching_sha2_password
,它是通过 sha256 算法来加密密码。
此插件为了使已连接的用户的身份验证速度更快,会在内存中缓存 MySQL Server 用户的认证信息。
具体参阅下图的英文介绍:
由于英文水平有限,加上这个英文介绍也不详细,我按照自己的意思来理解,大致意思是:
当 Java 程序通过 JDBC 驱动连接 MySQL 时,如果其它客户端之前已经登陆过该用户,那是会把该用户的验证信息缓存下来,此时即使Java客户端没有配置 RSA 公钥也可连接成功。
但是当MySQL服务被重启或配置修改后,用户认证缓存就被清掉了,所以导致”偶尔“报错的现象出现。
当连接之前没有认证缓存,或缓存被清掉后,这时再不指定 RSA 公钥,就会报错:Public Key Retrieval is not allowed
。
不过以上只是我的猜测,我想验证下我的想法,接下来搞一个测试。
测试过程
测试的main方法:
先再没有缓存的情况下,直接运行后,复现了我之前遇到的报错:
再用 mysql 客户端,用目标用户 user1 连接 MySQL Server,成功后就会有 user1 的认证缓存了:
接着再运行 java 测试方法,这回不报错了:
再试试重启 MySQL 数据库后再跑测试程序,又触发报错:
再次手工连接,虽然程序恢复正常:
清空用户缓存 flush privileges;
,再运行程序又报错:
切换MGR的场景也验证了缓存的作用,之前新的 Primary 节点一直没有应用用户连接过,也就一直没有过缓存,所以当应用程序连接新的 Primary 节点时会报错。
结论
和我的猜测一致,因为插件 caching_sha2_password
的存在导致用户连接MySQL服务后会缓存认证信息,只要同一用户的认证信息缓存存在,Java连接MySQL8.0时设不设置 RSA 公钥就看不出区别,但是MySQL重启、切换MGR等会清空缓存,所以这时没设置 RSA 公钥就会出现异常:Public Key Retrieval is not allowed
。
解决方案
择一即可:
- 指定 RSA 公钥;
- 设置参数 AllowPublicKeyRetrieval=True;
- 修改 MySQL Server 用户的密码加密插件为
mysql_native_password
;
本文由《MySql教程网》原创,转载请注明出处!https://mysql360.com