笔记
情况是使用mysql8版本的数据库,某天突然启动时就报错了Public Key Retrieval is not allowed
,然后可能又莫名其妙的好了。
刚开始别人说可能是jar版本不对没更新,感觉不像,我本地测试复现了几种情况,证实跟mysql-connector-java的版本无关
查了一篇文章写得比较详细 原文传送门=>
分析
出现这个报错的原因,大概就是因为mysql服务器版本升级之后,用户默认的密码存储方式是SHA256,由于安全限制,所以服务器会拒绝不安全的连接请求(默认)
在CachingSha2PasswordPlugin.java
源文件里:
// version: 8.0.29
if (!this.protocol.getPropertySet().getBooleanProperty(PropertyKey.allowPublicKeyRetrieval).getValue()) {
throw ExceptionFactory.createException(UnableToConnectException.class,
Messages.getString("Sha256PasswordPlugin.2"),
this.protocol.getExceptionInterceptor());
}
// version: 5.1.47
if (!this.connection.getAllowPublicKeyRetrieval()) {
throw SQLError.createSQLException(Messages.getString("Sha256PasswordPlugin.2"),
SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE,
this.connection.getExceptionInterceptor());
}
所以要么开启SSL安全连接验证,由安全协议来交互登录;
要么就要显式声明允许自动获取服务器公钥,让connector自动请求获取公钥,否则就会有Public Key Retrieval is not allowed
异常。
按传送门的说法,还有一种是,手动在mysql服务器上登录一次缓存用户访问控制数据(具体原理不清楚,大概是用户在mysql客户端可以直接登录(navicat等也算),然后服务端有用户访问控制列表,验证成功后会更新user的状态缓存,之后其他connector就可以直接连接了,具体原理还是不清楚,但是实测确实可以)。
但是感觉不可靠,随时可能被flush privileges
刷掉(复现测试,在服务器刷新权限后,直接使用connector依然会报错,每次都需要再重复一次上面的操作)。
所以解决方法有两个:
- 开启SSL,直接安全通信
useSSL=true
,缺点是配置相对麻烦,要提供证书秘钥等相关数据 - 显式声明允许自动获取服务器公钥数据
allowPublicKeyRetrieval=true
,缺点是安全性不足,可能被“中间人”拦截逆向获取明文密码(中间人攻击)。
扩展
基于上面的分析,其实之所以会突然出现上述问题(升级mysql版本后),还是因为用户密码的存储方式发生了变化。老版本可能设置的是mysql_native_password
,密码存储和验证就是原始密码,而改为sha2加密后,就有安全性限制了。
所以,也可以在创建用户/修改密码的时候,主动声明密码格式:
ALTER USER 'dev'@'host' IDENTIFIED WITH mysql_native_password BY 'pwd';
这样就跟老版本的使用没区别了。
上面几种情况,各中取舍就要看具体的需求了。