如何使用带有 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 客户端证书?
带有 http/2 和 ssl 的嵌入式 Tomcat 的 Spring Boot 2.1,gzip 不起作用