我有一个使用Spring Boot 1.0.0.RC5和tomcat 8.0.3的Web套接字(ws)非安全实现应用程序的工作示例。现在,我想切换到wss,即使用已经由tomcat加载的我自己的自签名证书。
这个问题分为两部分:理论部分和实践部分:
理论=>我需要让Tomcat监听两个端口吗?即在http和https上。我之所以这样问,是因为我读到在Web套接字通信期间,连接的第一部分是通过HTTP进行的,然后所谓的“升级”到Websocket。我正在发布测试示例
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才能使该构造起作用。
实际=>我面临的问题是负责创建踩踏消息的代码部分无法正常工作,即当我打开页面时
https://127.0.0.5:8888/wsTest
firefox给我打电话“此连接不受信任”,然后我告诉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),这应该是正确的行
我用命令生成了证书:
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套接字配置部分
@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>
使用的脚踩脚本版本为“ //由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://localhost:8888/wsTest
但是我仍然没有找到为什么会这样。此行为与chrome和firefox相同。
您绝对需要用于带SSL的Websocket的HTTPS连接器(即您的“ wss:// *”客户端)。可能由于证书问题而无法正常工作。如果您是我,请仔细检查浏览器配置中的证书例外情况。也许重新生成证书,然后重试。