带有Spring Boot 1.0.0.RC5和tomcat 8.0.3的HTTPS上的Websocket [英] Websockets over HTTPS with spring boot 1.0.0.RC5 and tomcat 8.0.3

查看:81
本文介绍了带有Spring Boot 1.0.0.RC5和tomcat 8.0.3的HTTPS上的Websocket的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a working example of the web socket (ws) non secure implementation application using spring boot 1.0.0.RC5 and tomcat 8.0.3. Now i would like to switch to wss i.e. using my own self signed certificate that had been loaded by tomcat already.

这个问题分为两个部分,一个是理论性的,一个是实践性的:

This question has two parts one theoretical and one practical:

理论=>我需要让tomcat在两个端口上侦听吗?即在http和https上.我之所以这样问,是因为我读到在Web套接字通信期间,连接的第一部分是通过HTTP进行的,然后所谓的升级"到Websocket.我正在发布测试示例

Theoretical => Do i need to have tomcat listening on two ports? i.e. on http and https. The reason i am asking this is because i read that during a web socket communication the first part of the connection is made over http and then there is so called "upgrade" to websockets. I am posting example of my test

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才能使该构造正常工作.

How this communication would look like over wss? Do we have the "upgrade" part as well, if so in this case we need http in order for that construct to work.

实用=>我面临的问题是负责创建重踩消息的代码部分无法正常工作,即,当我打开页面时

Practical => The problem i am facing is that the part of the code that is responsible for creating the stomp message is not working i.e. when i open the page

https://127.0.0.5:8888/wsTest

firefox给我打电话 此连接不受信任",然后我告诉Firefox我了解风险",并将证书添加为安全例外".从那时起,证书将存储在firefox的服务器"选项卡下. 到目前为止一切都很好.但是,当我更改为wss时,此游戏无法正常运行,正如我期望的那样.即,这是在客户端上创建套接字的函数.

firefox tels me "This Connection is Untrusted" then i tell firefox "I understand the risk" and add the certificate as "Security Exception". From that point the certificate is stored under the firefox "servers" tab. All good till now. However when i change to wss this game does not work as i expected it to work. i.e. This is the function that is creating the socket on the client side.

     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"); 

然后所有工作再次进行.问题似乎出在线路上

then all works again. The problem seems to be with the line

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

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

however according to this bug note (https://jira.spring.io/browse/SPR-11436) this should be the correct line

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

I have generated the ceritficate with the command:

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);


            }
        };
    }

这是Web套接字配置部分

here is the web socket configuration portion

@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调试控制台上看到的其他消息

additional messages seen on the firefox debug console

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页面的完整版本

here is full version of the html page

<!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>

使用的脚踩脚本版本为"//由CoffeeScript 1.6.3生成"

the stomp script version used is "// Generated by 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):  

添加:我还注意到,如果我拨打电话

Addition: I have also noticed that web sockets are working if i call

https://127.0.0.5:8888/wsTest  

但如果我打电话

https://localhost:8888/wsTest

但是我仍然没有找到为什么会这样. chrome和firefox的行为相同.

however i still have not found why is this happening. This behavior is the same with chrome and firefox.

推荐答案

您肯定需要一个用于带SSL的Websocket的HTTPS连接器(即您的"wss://*"客户端).可能由于证书问题而无法正常工作.如果您是我,请仔细检查浏览器配置中的证书例外情况.也许重新生成证书,然后重试.

You definitely need an HTTPS connector for Websockets with SSL (I.e. your "wss://*" client). It's possible it isn't working because of the certificate problem. If I were you I'd double check the browser configuration for the certificate exceptions. Maybe regenerate the certificate and try again.

这篇关于带有Spring Boot 1.0.0.RC5和tomcat 8.0.3的HTTPS上的Websocket的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆