来一杯苹果汁 2021-12-06 07:10 采纳率: 0%
浏览 494
已结题

SpringBoot项目中使用RestTemplate访问部分HTTPS服务报SocketException: Connection Rest错误

问题遇到的现象和发生背景

最近公司有一个稍微旧一点的项目,使用Java 8和SpringBoot 1.5.8.RELEASE版本,由于SpringBoot 1.0版本不支持WebClient, 所以只能使用RestTemplate进行连接(公司现有的使用SpingBoot 2.0版本以上的其它项目都在使用WebClient)。使用RestTemplate访问公司使用http协议的服务器时,没有任何错误,可以得到200的响应;但是在当前项目里使用RestTemplate访问https的服务器(使用self-signed certificate)时,总会报“javax.net.ssl.SSLException: java.net.SocketException: Connection reset”这个错误。这个项目中之前也用RestTemplate连接过https的服务器,我试了他们之前用到的那个URL,没有报错,但是连我现在要用到的这个URL时就会出现问题。即便如此,可能也不是我要连接的服务器方面的错误,具体在下面会谈到。

问题相关代码,请勿粘贴截图

以下配置尝试过都会产生SocketException: Connection Rest

关闭SSL验证之后的RestTemplate的配置:

 @Bean(name = "RestConnector")
    public  RestTemplate createRestTemplate1() {
        TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
        HostnameVerifier hostnameVerifier = (s, session) -> true;
        SSLContext sslContext = null;
        try {
             sslContext = SSLContexts.custom()
                    .loadTrustMaterial(null, acceptingTrustStrategy)
                    .build();
        } catch (Exception e) {
            e.printStackTrace();
        }
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);
        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(csf)
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory =
                new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        return new RestTemplate(requestFactory);
    }

将公司的证书导入到RestTemplate的配置:

@Bean(name = "JKSConnector")
    public RestTemplate getRdsJKS1() {
        InputStream ins = null;
        RestTemplate restTemplate = null;
        try {
            TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
            HostnameVerifier hostnameVerifier = (s, session) -> true;
            ins = keyStorePath.getInputStream();
            String tempPath ="/tmp/client.jks";
            File targetFile = new File(tempPath);
            FileUtils.copyInputStreamToFile(ins, targetFile);
            SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(new SSLContextBuilder()
                    .loadTrustMaterial(targetFile, keyStorePassWord.toCharArray(), acceptingTrustStrategy)
                    .build());
            HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).setSSLHostnameVerifier(hostnameVerifier).build();
            HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
            restTemplate = new RestTemplate(factory);
        } catch (IOException | NoSuchAlgorithmException | KeyStoreException | CertificateException | KeyManagementException e) {
            LOG.error("Exception occurred when building Resttemplate: -{}", e);
        } finally {
            if(ins != null) {
                try {
                    ins.close();
                } catch (IOException e) {
                    LOG.error("Exception when closing input stream: -{}", e);
                }
            }
        }
        return restTemplate;
    }


这个项目中之前用到的关闭SSL验证之后的配置:

@Bean(name = "RDSRestConnector")
    public  RestTemplate createRestTemplate() {
        TrustManager[] UNQUESTIONING_TRUST_MANAGER = new TrustManager[]{
                new X509TrustManager() {
                    public java.security.cert.X509Certificate[] getAcceptedIssuers() {
                        return null;
                    }

                    public void checkClientTrusted(X509Certificate[] certs, String authType) {
                    }

                    public void checkServerTrusted(X509Certificate[] certs, String authType) {
                    }
                }
        };
        SSLContext sslContext = null;
        try {
            sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, UNQUESTIONING_TRUST_MANAGER, null);
            HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
        } catch (KeyManagementException | NoSuchAlgorithmException e) {
            System.out.println(e);
        }
        SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
        CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);
        LOG.info("Creating custom RestTemplate successful ");
        return new RestTemplate(requestFactory);
    }

运行结果及报错内容

访问带有self-signed证书的https的服务器时报一下错误:

SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://*****.net/*****/**/*******": java.net.SocketException: Connection reset; nested exception is javax.net.ssl.SSLException: java.net.SocketException: Connection reset] with root cause
java.net.SocketException: Connection reset
    at java.net.SocketInputStream.read(SocketInputStream.java:210)
    at java.net.SocketInputStream.read(SocketInputStream.java:141)
    at sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:475)
    at sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:469)
    at sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:159)
    at sun.security.ssl.SSLTransport.decode(SSLTransport.java:110)
    at sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1279)
    at sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1188)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:401)
    at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:373)
    at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:587)
    at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
    at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:167)
    at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:78)
    at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
    at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:652)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
    at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
    at com.*****.service.TestConnectService.executeGet2(TestConnectService.java:88)
    at com.*****.controller.TestRdsController.execute(TestRdsController.java:32)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:967)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:901)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at com.******.CorsFilterConfig.doFilter(CorsFilterConfig.java:43)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:208)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:347)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:263)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:199)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1459)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)

我的解答思路和尝试过的方法

我觉得配置代码没有问题,所以为了检查这个错误,我自己新建了一个springboot的测试项目,这个测试项目中用到的dependencies和springboot版本都和出问题的项目保持一致。结果是同样的代码,在测试项目中连接公司的https服务器没有任何问题,甚至不用配置RestTemplate也可以使用(直接new)。只有在出问题的这个项目中才会一直有connection reset这个错误。

这个问题尝试过不同的解决方案:

  1. 最开始配置看见这个错误的时候,由于之前在公司的其它项目中遇到过因为证书不可靠出现的SSL验证问题(不同点是使用WebClient),所以想在RestTemplate中关闭SSL的验证,配置代码如以上所示,最后仍然报相同的错误。
  2. 配置公司的proxy,这个proxy在其它项目中也有用到,但是在现在这个项目中报HandshakeException。
  3. 将公司的证书(.crt)转换成一个jks文件,导入到RestTemplate中,配置过程如之前代码所示。仍然有connection reset错误。
  4. 咨询过相关团队,他们让我下载了公司的证书,生成pem文件导入到Java 8的JVM中,仍然报错。之前在最开始有提到测试项目,在测试项目中即使使用Java 11也可以正常连接https服务器(本地的Java 11中没有安装pem证书,仅在Java 8中有)。
  5. 尝试在http header中设置长连接和短连接,报错。
  6. 尝试升级springboot版本到2.0,使用和其它项目中用到的一样的WebClient和相同的配置尝试连接,仍然报connection reset错误。
  7. 尝试报错后进行retry,报错。
  8. IntelliJ IDEA中没有配置proxy.
  9. 怀疑是SSL版本问题,尝试在socketFactory配置了对应的版本(从postman中得到信息使用“TLSv1.3”), 结果connection reset:
 SSLConnectionSocketFactory socketFactory =
                new SSLConnectionSocketFactory(sslContextBuilder.build(),  new String[]{"TLSv1","TLSv1.1","TLSv1.2","SSLv3"}, null, NoopHostnameVerifier.INSTANCE);
  1. 尝试使用RestTmplate的不同方法,如getForObject, getForEntity, exchange等,报相同的错误。
  2. 尝试询问当初写这个项目的团队是否配置了什么东西阻止了访问,对方说应该没有。项目中其它地方也有用到RestTemplate来访问https服务,经过测试,他们之前项目里用到的https地址不会报错,而我现在要用的这个地址就会出现问题。
  3. Postman中不需要添加任何证书也可以直接访问要连接的endpoint得到结果。
我想要达到的结果

这个问题是在于相同的代码在我新建的测试项目中完全可以使用,测试项目所用的依赖和spring版本都和老项目完全相同,甚至RestTemplate直接new不用配置也可以使用,但在放到这个目前需要完成的老项目中就会一直报connection reset错误。

希望可以知道怎么解决这个问题,肯定是项目中哪里出现了问题,但没看到相关的配置啊,希望得到一些指导!

以上错误均发生在本地(local),不在云端。

  • 写回答

2条回答 默认 最新

  • 有问必答小助手 2021-12-07 10:13
    关注

    你好,我是有问必答小助手,非常抱歉,本次您提出的有问必答问题,技术专家团超时未为您做出解答


    本次提问扣除的有问必答次数,已经为您补发到账户,我们后续会持续优化,扩大我们的服务范围,为您带来更好地服务。

    评论

报告相同问题?

问题事件

  • 系统已结题 12月14日
  • 创建了问题 12月6日

悬赏问题

  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料