使用 Spring Boot 1.0.0.RC5 和 tomcat 8.0.3 通过 HTTPS 进行 Websockets

2024-04-05

我有一个使用 spring boot 1.0.0.RC5 和 tomcat 8.0.3 的 Web 套接字(ws)非安全实现应用程序的工作示例。 现在我想切换到 wss,即使用我自己的自签名证书,该证书已由 tomcat 加载。

这个问题有两部分,一是理论,一是实践:

理论 => 我需要让 tomcat 监听两个端口吗?即在 http 和 https 上。我问这个问题的原因是因为我读到在 Web 套接字通信期间,连接的第一部分是通过 http 建立的,然后有所谓的“升级”到 Web 套接字。我正在发布我的测试示例

GET /hello HTTP/1.1
Host: 127.0.0.5:8080
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:28.0) Gecko/20100101 Firefox/28.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en,en-gb;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Sec-WebSocket-Version: 13
Origin: http://localhost:8080
Sec-WebSocket-Key: wIKSOMgaHbEmkaRuEHZ6IA==
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket


HTTP/1.1 101 Switching Protocols
Server: Apache-Coyote/1.1
Upgrade: websocket
Connection: upgrade
Sec-WebSocket-Accept: 8trj6dyzlYdDUA3nAuwiM7SijRM=
Date: Mon, 31 Mar 2014 10:29:19 GMT


..,O.do..*i..nM,..\;..I=.
C!.U.~.U....I....-..Xu.T...H...T.E.d
.'
CONNECTED
heart-beat:0,0
version:1.1

.
.....]..M...F...f9..z?...9..{4..{4..5r...4..h/..{4..|W..

通过 wss 进行的通信会是什么样子?我们是否也有“升级”部分,如果有的话,在这种情况下我们需要 http 才能使该结构正常工作。

实用=>我面临的问题是负责创建stomp消息的代码部分不起作用,即当我打开页面时

https://127.0.0.5:8888/wsTest

火狐告诉我 “此连接不受信任”,然后我告诉 Firefox“我了解风险”并将证书添加为“安全异常”。从那时起,证书将存储在 Firefox“服务器”选项卡下。 到现在为止一切都好。然而,当我切换到 wss 时,这个游戏无法像我预期的那样工作。即这是在客户端创建套接字的函数。

     function connect() {
            var socket = new WebSocket("wss://127.0.0.5:8888/hello");

            stompClient = Stomp.over(socket);
            stompClient.connect({}, function(frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/topic/greetings', function(greeting){
                    showGreeting(JSON.parse(greeting.body).content);
                });
            });
        } 

如果我改变线路

var socket = new WebSocket("wss://127.0.0.5:8888/hello"); 

到 http 版本,即

var socket = new WebSocket("ws://127.0.0.5:8080/hello"); 

然后一切又恢复正常了。问题似乎出在线路上

stompClient.connect({}, function(frame) {

但是根据这个错误注释(https://jira.spring.io/browse/SPR-11436 https://jira.spring.io/browse/SPR-11436)这应该是正确的行

我已经使用以下命令生成了证书:

keytool -genkey -alias tomcat -keyalg RSA  -keystore /home/tito/Projects/syncServer/Server/certificate/sync.keystore

服务器端:

@Configuration
public class TomcatEmbeded extends SpringServletContainerInitializer {


    final int http_port = 8080;
    final int https_port = 8888;
    final String keystoreFile = "/home/tito/Projects/syncServer/Server/certificate/sync.keystore";
    final String keystorePass = "changeit";
    final String keystoreType = "JKS";
    final String keystoreProvider = "SUN";
    final String keystoreAlias = "tomcat";
    final String https_scheme = "https";
    final String http_scheme = "http";

    @Bean
    public EmbeddedServletContainerFactory servletContainer() {


        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory(http_port);

        factory.setTomcatContextCustomizers(
                Arrays.asList (
                        new TomcatContextCustomizer[]{ 
                                tomcatContextCustomizer() 
                                } 
                            )
                        );

        factory.addConnectorCustomizers( new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector con) {
                    Http11NioProtocol proto = (Http11NioProtocol) con.getProtocolHandler();

                    try {

                        con.setPort(https_port);
                        con.setSecure(true);
                        con.setScheme("https");
                        con.setAttribute("keyAlias", keystoreAlias);
                        con.setAttribute("keystorePass", keystorePass.toString());
                        try {
                            con.setAttribute("keystoreFile", ResourceUtils.getFile(keystoreFile).getAbsolutePath());
                        } catch (FileNotFoundException e) {
                            throw new IllegalStateException("Cannot load keystore", e);
                        }

                        con.setAttribute("clientAuth", "false");
                        con.setAttribute("sslProtocol", "TLS");
                        con.setAttribute("SSLEnabled", true);

                        proto.setSSLEnabled(true);
                        proto.setKeystoreFile(keystoreFile);
                        proto.setKeystorePass(keystorePass);
                        proto.setKeystoreType(keystoreType);
                        proto.setProperty("keystoreProvider", keystoreProvider.toString());
                        proto.setKeyAlias(keystoreAlias);

                    } catch (Exception ex) {
                        throw new IllegalStateException("can't access keystore: [" + "keystore"
                                + "] or truststore: [" + "keystore" + "]", ex);
                    }
                    System.out.println("INIT HTTPS");

                }
            }
        );


        factory.addAdditionalTomcatConnectors(httpConnector());
//      factory.addErrorPages(new ErrorPage(HttpStatus.404, "/notfound.html");
        System.out.println("TOMCAT CUSTOME SETTINGS INITILIZED");

        return factory;
    }


    private Connector httpConnector() {
        Connector connector = new Connector();
        connector.setScheme(this.http_scheme);
        connector.setSecure(true);
        connector.setPort(this.http_port);
        System.out.println("INIT port HTTP");
        return connector;
    }


    @Bean 
    public TomcatContextCustomizer tomcatContextCustomizer() {
        System.out.println("TOMCATCONTEXTCUSTOMIZER INITILIZED");
        return new TomcatContextCustomizer() {

            @Override
            public void customize(Context context) {
                // TODO Auto-generated method stub
                context.addServletContainerInitializer(new WsSci(), null);


            }
        };
    }

这是网络套接字配置部分

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic");
        config.setApplicationDestinationPrefixes("/app");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/hello");
    }


    @Override
    public void configureClientInboundChannel(ChannelRegistration channelRegistration) {
    }

    @Override
    public void configureClientOutboundChannel(ChannelRegistration channelRegistration) {
    }

    @Override
    public boolean configureMessageConverters(List<MessageConverter> arg0) {

        return true;
    } 

Firefox 调试控制台上看到的其他消息

Use of getUserData() or setUserData() is deprecated.  Use WeakMap or element.dataset instead. requestNotifier.js:64
"Opening Web Socket..." stomp.js:130
Firefox can't establish a connection to the server at wss://127.0.0.5:8888/hello. wsTest:18
"Whoops! Lost connection to wss://127.0.0.5:8888/hello" stomp.js:130

这是 html 页面的完整版本

<!DOCTYPE html>
<html>
<head>
    <title>Hello WebSocket</title>
    <script src="/js/stomp.js"></script>
    <script type="text/javascript">
        var stompClient = null;

        function setConnected(connected) {
            document.getElementById('connect').disabled = connected;
            document.getElementById('disconnect').disabled = !connected;
            document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden';
            document.getElementById('response').innerHTML = '';
        }

        function connect() {
            var socket = new WebSocket("wss://127.0.0.5:8888/hello");

            stompClient = Stomp.over(socket);
 //           stompClient.connect('tito', 'password', function(frame) {
            stompClient.connect({}, function(frame) {
                setConnected(true);
                console.log('Connected: ' + frame);
                stompClient.subscribe('/topic/greetings', function(greeting){
                    showGreeting(JSON.parse(greeting.body).content);
                });
            });
        }

        function disconnect() {
            stompClient.disconnect();
            setConnected(false);
            console.log("Disconnected");
        }

        function sendName() {
            var name = document.getElementById('name').value;
            stompClient.send("/app/hello", {}, JSON.stringify({ 'name': name }));
        }

        function showGreeting(message) {
            var response = document.getElementById('response');
            var p = document.createElement('p');
            p.style.wordWrap = 'break-word';
            p.appendChild(document.createTextNode(message));
            response.appendChild(p);
        }
    </script>
</head>
<body>
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being enabled. Please enable
    Javascript and reload this page!</h2></noscript>
<div>
    <div>
        <button id="connect" onclick="connect();">Connect</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
    </div>
    <div id="conversationDiv">
        <label>What is your name?</label><input type="text" id="name" />
        <button id="sendName" onclick="sendName();">Send</button>
        <p id="response"></p>
    </div>
</div>
</body>
</html>

使用的stomp脚本版本是“//由CoffeeScript 1.6.3生成”

这是证书的生成方式

$ keytool -genkey -alias tomcat -keyalg RSA  -keystore /home/tito/Projects/syncServer/Server/certificate/sync.keystore
Enter keystore password:  
Re-enter new password: 
What is your first and last name?
  [Unknown]:  TestFirstName
What is the name of your organizational unit?
  [Unknown]:  TestOrganizationalUnitName
What is the name of your organization?
  [Unknown]:  TestOrganization
What is the name of your City or Locality?
  [Unknown]:  TestCity
What is the name of your State or Province?
  [Unknown]:  TestState
What is the two-letter country code for this unit?
  [Unknown]:  BG
Is CN=TestFirstName, OU=TestOrganizationalUnitName, O=TestOrganization, L=TestCity, ST=TestState, C=BG correct?
  [no]:  yes

Enter key password for <tomcat>
        (RETURN if same as keystore password):  

另外:我还注意到,如果我打电话,网络套接字正在工作

https://127.0.0.5:8888/wsTest  

但如果我打电话就不工作

https://localhost:8888/wsTest

但是我仍然没有找到为什么会发生这种情况。此行为与 Chrome 和 Firefox 相同。


您肯定需要一个用于具有 SSL 的 Websockets 的 HTTPS 连接器(即您的“wss://*”客户端)。由于证书问题,它可能无法正常工作。如果我是你,我会仔细检查浏览器配置是否有证书例外。也许重新生成证书并重试。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 Spring Boot 1.0.0.RC5 和 tomcat 8.0.3 通过 HTTPS 进行 Websockets 的相关文章

随机推荐