最近在做面向k8s的项目镜像,遇到很奇怪的问题,SpringCloud 项目连接mysql 抛 javax.net.ssl.SSLHandshakeException: No appropriate protocol,调查一段时间后,发现是Java security中的配置不对导致连接Mysql异常。
javax.net.ssl.SSLHandshakeException: No appropriate protocol (protocol is disabled or cipher suites are inappropriate) at sun.security.ssl.HandshakeContext.<init>(HandshakeContext.java:171) ~[na:1.8.0_292] at sun.security.ssl.ClientHandshakeContext.<init>(ClientHandshakeContext.java:98) ~[na:1.8.0_292] at sun.security.ssl.TransportContext.kickstart(TransportContext.java:220) ~[na:1.8.0_292] at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:428) ~[na:1.8.0_292] at com.mysql.cj.protocol.ExportControlled.performTlsHandshake(ExportControlled.java:316) ~[mysql-connector-java-8.0.17.jar:8.0.17] at com.mysql.cj.protocol.StandardSocketFactory.performTlsHandshake(StandardSocketFactory.java:188) ~[mysql-connector-java-8.0.17.jar:8.0.17] at com.mysql.cj.protocol.a.NativeSocketConnection.performTlsHandshake(NativeSocketConnection.java:99) ~[mysql-connector-java-8.0.17.jar:8.0.17] at com.mysql.cj.protocol.a.NativeProtocol.negotiateSSLConnection(NativeProtocol.java:331) ~[mysql-connector-java-8.0.17.jar:8.0.17] ... 68 common frames omitted
看到SSLHandshakeException,心里打起了问号?这个错误比较反常,最终网上找了一番,问题定位了:JDK8高版本导致的,因为之前用的是oracle:1.8,然后换成了openjdk:8,然后发现项目无法启动了。
方法一:
此处连接的MySQL,导致的报错,修改jdbcUrl,在其后面加useSSL=false后运行正常
方法二:
删除SSLv3, TLSv1, TLSv1.1并保存java.security文件,重启项目即可解决问题,删除后此处为:
# Example: # jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048 jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4, DES, MD5withRSA, \ DH keySize < 1024, EC keySize < 224, 3DES_EDE_CBC, anon, NULL, \ include jdk.disabled.namedCurves
方法三:
降低JDK版本,这个相当也容易操作,比如可以从1.8.0_292降到1.8.0_281,甚至是1.8.0_275版本,但个人不建议,因为Oracle对JDK 8的支持一直会到2030年:即使很长一段时间用JDK 8,但JDK 8本身也是有小版本迭代的,比如你明年换了电脑,安装JDK,基本是1.8.0_292之后的版本,那这个问题会一直存在。
方法四:
方法一 能解决由SSL调用权限导致的所有问题,但破坏了安全性
方法二 针对MySQL的问题,可以快速解决
方法三 不推荐
因此,碰到类似问题,基本的思路是兼容JDK 8高版本甚至JDK高版本,比如代码层面的:
private Socket overrideTlsProtocol(final Socket socket) { if (!(socket instanceof SSLSocket)) { throw new RuntimeException("Error, an instance of SSLSocket is expected"); } ((SSLSocket) socket).setEnabledProtocols(new String[]{"SSLv3"}); return socket; }
修改为:
private Socket overrideTlsProtocol(final Socket socket) { if (!(socket instanceof SSLSocket)) { throw new RuntimeException("Error, an instance of SSLSocket is expected"); } ((SSLSocket) socket).setEnabledProtocols(new String[]{"SSLv3", "TLSv1","TLSv1.1"}); return socket; }