JDK8版本过高引起MySQL连接失败:javax.net.ssl.SSLHandshakeException: No appropriate protocol

14,368 阅读2分钟

前几天发现一直跑的项目本地无法启动了:

com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure

The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
	at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:827) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:447) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:237) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:199) ~[mysql-connector-java-8.0.17.jar:8.0.17]
	at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:136) ~[HikariCP-3.2.0.jar:na]
	at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:369) ~[HikariCP-3.2.0.jar:na]
	at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:198) ~[HikariCP-3.2.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:467) [HikariCP-3.2.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:541) [HikariCP-3.2.0.jar:na]
	at com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115) [HikariCP-3.2.0.jar:na]
	at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:112) [HikariCP-3.2.0.jar:na]
	at com.zaxxer.hikari.HikariDataSource$$FastClassBySpringCGLIB$$eeb1ae86.invoke(<generated>) [HikariCP-3.2.0.jar:na]

看到Communications link failure初步认为是超时了,网络问题导致的,但在解决了网络问题后发现依旧报相同的错,这就奇怪了,然后一条一条的看报错日志,找到:

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,心里打起了问号?这个错误比较反常,最终网上找了一番,问题定位了:

JDK 8高版本导致的,之前用的1.8.0_281,前些天捣鼓Homebrew,发现JDK 8有新版本,就升级了1.8.0_292,然后发现项目无法启动了。

查看/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/lib/security/java.security(Mac上;Windows同理,在jre/lib/security/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
  • 方法一:

    此处连接的MySQL,导致的报错,修改jdbcUrl,在其后面加useSSL=false后运行正常

  • 方法二:

    删除SSLv3, TLSv1, TLSv1.1并保存java.security文件,重启项目即可解决问题,删除后此处为:

    # Example:
    #   jdk.tls.disabledAlgorithms=MD5, SSLv3, DSA, RSA keySize < 2048
    jdk.tls.disabledAlgorithms=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年:

image-20210602182131342.png

即使很长一段时间用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;
    }
    

有问题直面问题、解决问题,不要拍折腾,经常捣鼓这些东西,相信你也会受益匪浅,希望对你有所帮助~😄😄😄