java - 与服务连接时缺少客户端证书链问题

标签 java spring-boot concurrency ibm-mq

有一个与基于 SOAP 的服务连接的 Spring Boot 应用程序。我们的应用程序有一个 MQ 监听器,用于从本地队列接收消息。一旦消息从该队列中出列,它就会对服务进行 API 调用。

与基于 SOAP 的服务的所有通信都是通过 2 路 SSL 完成的。

现在我们在连接基于 SOAP 的服务时遇到了问题。基于 SOAP 的服务端间歇性收到缺少客户端证书链错误。

在我们的 JmsConfig 类中,我们将监听器工厂的并发性设置为 10,如下所示,以便并发执行和并行处理来自队列的消息。

 @Bean
 public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(){
   DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
   try {
          factory.setConnectionFactory(cachingConnectionFactory(jmsConnectionFactoryX20()));
          factory.setConcurrency("10");
   } catch (JMSException e) {
          e.printStackTrace();
   }
   return factory;
 }

当我们在消息被使用后进一步从 Spring Boot 应用程序调用 SOAP 服务时,大多数消息的 SOAP 服务日志中都会出现缺少客户端证书链错误。在队列中批量加载 1k 条消息并随后并发执行时,大约 850 条消息在 SOAP 服务端失败,只有 150 条通过。

仅当以编程方式设置并发时才会出现错误,如上面的代码片段所示。如果我注释掉 setConcurrency 调用,所有 1k 消息都会在 SOAP 服务端得到处理,而不会出现任何证书链错误。

您能否看一下我们的配置并分享一些关于并发性导致此错误的原因的想法。

ApplicationWebServiceImpl.java(从 JMS 监听器方法调用 callWebService() 方法)

  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.beans.factory.annotation.Value;
  import org.springframework.stereotype.Service;
  import org.springframework.util.StringUtils;
  import org.springframework.ws.client.core.WebServiceTemplate;
  import org.springframework.ws.soap.SoapMessage;

  @Service
  public class ApplicationWebServiceImpl implements ApplicationWebServices {

  @Autowired
  private WebServiceTemplate wsTemplate;

  @Value("${soap.clientEndpoint}")
  private String clientEndpoint;

  @Value("${soap.action}")
  private String soapAction;

  @Override
  public Object callWebService(Object request, String serviceName) throws ClientApplicationException {

      return wsTemplate.marshalSendAndReceive(clientEndpoint + serviceName, request, message -> {
          SoapMessage msg = (SoapMessage) message;
          msg.setSoapAction(soapAction + StringUtils.capitalize(serviceName));
      });
  }
}

ClientApplication(Spring启动主类)

@SpringBootApplication
@ComponentScan("com.abc.ca")    
public class ClientApplication {

  @Value(value = "${cert.keyStoreFileName}")
  private String keyStoreFileName;

  @Value(value = "${cert.trustStoreFileName}")
  private String trustStoreFileName;

  @Value(value = "${cert.disabledAlgorithms}")
  private String disabledAlgorithms;

  @PostConstruct
  public void getKeyTrustStore() {
      Security.setProperty("jdk.certpath.disabledAlgorithms", disabledAlgorithms);
      String filePath = Thread.currentThread().getContextClassLoader().getResource(keyStoreFileName).getFile();

      System.setProperty("javax.net.ssl.keyStore", filePath);
      System.setProperty("javax.net.ssl.keyStorePassword", "changeme");
      filePath = Thread.currentThread().getContextClassLoader().getResource(trustStoreFileName).getFile();      

      System.setProperty("javax.net.ssl.trustStore", filePath);
      System.setProperty("javax.net.ssl.trustStorePassword", "changeme");
  }

  public static void main(String[] args) {
      SpringApplication.run(ClientApplication.class, args);
  }
}

最佳答案

更新一下,我通过在 wsTemplate 上显式调用 setMessageSender 并设置 HttpsUrlConnectionMessageSender 解决了上述问题。

感谢@chughts 给我指点。

干杯!

 wsTemplate.setMessageSender(httpsUrlConnectionMessageSender());

 @Bean 
 public HttpsUrlConnectionMessageSender httpsUrlConnectionMessageSender() throws Exception { 
     HttpsUrlConnectionMessageSender httpsUrlConnectionMessageSender = new 
                HttpsUrlConnectionMessageSender();

    httpsUrlConnectionMessageSender.setTrustManagers(
            loadDefaultTrustStore());

    httpsUrlConnectionMessageSender.setKeyManagers(
            loadKeyStore());

    httpsUrlConnectionMessageSender.setHostnameVerifier(new HostnameVerifier() { 
       @Override 
       public boolean verify(String hostname, javax.net.ssl.SSLSession sslSession) { 
          if ("localhost".equals(hostname)) { 
            return true; 
          } 
          return false; 
       } 
    });     
    return httpsUrlConnectionMessageSender; 
}

private KeyManager[] loadKeyStore() throws KeyStoreException,
     FileNotFoundException, IOException, NoSuchAlgorithmException,
     CertificateException, UnrecoverableKeyException {

     KeyStore keyStore = KeyStore.getInstance("jks");

     FileInputStream fileInputStreamKeyStore = new FileInputStream("/ca/appKeystore.jks");
     keyStore.load(fileInputStreamKeyStore,"changeme".toCharArray());

     KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory
            .getDefaultAlgorithm());        
     keyFactory.init(keyStore,"changeme".toCharArray());
     return keyFactory.getKeyManagers();
}

private TrustManager[] loadDefaultTrustStore() throws NoSuchAlgorithmException,
                KeyStoreException {
     TrustManagerFactory trustFactory = 
     TrustManagerFactory.getInstance(TrustManagerFactory
        .getDefaultAlgorithm());
     trustFactory.init((KeyStore)null);     
     return trustFactory.getTrustManagers();
}

关于java - 与服务连接时缺少客户端证书链问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59614064/

相关文章:

java - Apache Camel : Aws-S3 consumer starts failing with connection pool timeout

java - Spring Boot 关闭钩子(Hook)

java - spring框架的yml配置文件

Java RMI 和同步方法

java - 在 If 语句中比较字符串时出错

java - Azure IoT 中心设备消息、消息正文上的路由筛选器不起作用

java - 如何通过Java编码调用浏览器?

java - Spring Boot 中的 "Unsupported Media Type"- Windows

Java ConcurentMap keySet() 修改 map 并迭代键集时的问题

java - 当两个并发线程尝试从 CopyOnWriteArrayList 中删除元素时会发生什么? java