如何使用带有 SSL 的 Spring WebSocketClient?

Posted

技术标签:

【中文标题】如何使用带有 SSL 的 Spring WebSocketClient?【英文标题】:How to use Spring WebSocketClient with SSL? 【发布时间】:2015-11-17 14:40:03 【问题描述】:

我用我的 websocket 客户端连接到非 SSL 端点没有任何问题。但我找不到任何方法连接到 wss (SSL) 端点。我在哪里可以定义 SSL 工厂等。似乎没有对象有相关的 set 方法。

WebSocketClient transport = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(transport);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
String url = cfg.getWebsocketEndpoint();
StompSessionHandler handler = new MySessionHandler();
WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
stompClient.connect(url, handler);

我使用的是 wss:// url,而另一方面我有一个带有自签名证书的服务器。但是,这段代码在连接时并没有抛出任何异常,只是没有建立会话。

编辑:启用网络跟踪后。* 我遇到了一个标准错误,其中

sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

在使用自签名证书连接到服务器时发生。但是,对于 RestTemplate,我已经使用此代码更新了 SSLContext,REST 调用现在很好,但我不知道为什么,StandardWebSocketClient 忽略了 SSLContext。为什么?

    String keystoreType = "JKS";
    InputStream keystoreLocation = new FileInputStream("src/main/resources/aaa.jks");
    char [] keystorePassword = "zzz".toCharArray();
    char [] keyPassword = "zzz".toCharArray();

    KeyStore keystore = KeyStore.getInstance(keystoreType);
    keystore.load(keystoreLocation, keystorePassword);
    KeyManagerFactory kmfactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    kmfactory.init(keystore, keyPassword);

    InputStream truststoreLocation = new FileInputStream("src/main/resources/aaa.jks");
    char [] truststorePassword = "zzz".toCharArray();
    String truststoreType = "JKS";

    KeyStore truststore = KeyStore.getInstance(truststoreType);
    truststore.load(truststoreLocation, truststorePassword);
    TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    tmfactory.init(truststore);

    KeyManager[] keymanagers = kmfactory.getKeyManagers();
    TrustManager[] trustmanagers =  tmfactory.getTrustManagers();

    SSLContext sslContext = SSLContext.getInstance("TLS");
    sslContext.init(keymanagers, trustmanagers, new SecureRandom());
    SSLContext.setDefault(sslContext);

更新:不幸的是,我没有设法通过自定义信任库做到这一点。我使用 InstallCert.java 安装了证书。

【问题讨论】:

【参考方案1】:

我认为每个 websocket 容器实现都提供了实现这一点的方法。 您必须使用StandardWebSocketClient .setUserProperties 设置此配置。所有这些属性都在客户端使用的ClientEndpointConfig 内部设置。

这是一个使用 Tomcat 作为提供者的示例:

StandardWebSocketClient wsClient = //...;
SSLContext sslContext = //...;
wsClient.setUserProperties(WsWebSocketContainer.SSL_CONTEXT_PROPERTY, sslContext);

在任何情况下,您都应该参考您的供应商参考文档以了解您应该使用哪些配置密钥。

【讨论】:

你知道 Tyrus 的这个设置吗? 我最终将我的自签名证书导入密钥库并使用它。 digz6666,你能告诉 - 你是怎么做到的吗? 到底有没有禁用tomcat的主机名验证?我没有找到任何例子。 @niegus 这是一个新问题的材料。请问可以吗?【参考方案2】:

我遇到了类似的问题,我需要使用不同的 ssl 上下文而不是默认上下文,并且我无法使用 Tomcat 提供程序版本解决方案。我遇到了很多关于此解决方案的资源和示例,这些资源和示例不适合我的情况,而且我肯定也遇到了这个问题。

当您需要使用特定/自定义 SSLContext 进行设置并且不需要“tomcat 版本”时,您可以选择 Jetty 版本,因为它允许您设置所需的信任库和/或密钥库使用。在这种情况下,我的 Spring 应用程序位于“客户端”而不是服务器端,但 Jetty SslContextFactory 为“服务器”案例提供了相同的功能。

下面的简短示例代码用于客户端,但它与服务器端共享方法和签名 (check the jetty documentation about)

final SslContextFactory.Client factory = new SslContextFactory.Client();
factory.setSslContext(sslContext); // a different loaded java SslContext instead of the default one

//Create the web socket client with jetty client factory
final JettyWebSocketClient client =
      new JettyWebSocketClient(new WebSocketClient(new HttpClient(factory)));
client.start();
final WebSocketStompClient stomp = new WebSocketStompClient(client);

还有设置信任存储或密钥存储的方法,而不是完全加载的 SslContext。

【讨论】:

以上是关于如何使用带有 SSL 的 Spring WebSocketClient?的主要内容,如果未能解决你的问题,请参考以下文章

运行带有 SSL 和同时未加密的 Spring Boot 应用程序(嵌入式 Tomcat)

Spring Boot SSL 客户端

如何配置 Spring Boot 应用程序以接受未知的 SSL 客户端证书?

带有 http/2 和 ssl 的嵌入式 Tomcat 的 Spring Boot 2.1,gzip 不起作用

使用 Hibernate、Spring 和 JDBC 配置 SSL 证书

如何使用带有 SSL 的 httpclient 调用 SOAP Web 服务