【问题标题】:Secured WebSocket upgrade over STOMP via SockJS fails with Invalid Upgrade header null通过 SockJS 通过 STOMP 进行安全 WebSocket 升级失败,升级标头无效
【发布时间】:2014-12-14 16:00:31
【问题描述】:

我正在开发一个使用 Spring Security 和 WebSockets 的 Web 应用程序。我可以在本地机器上毫无问题地使用 WebSocket,从带有嵌入式 Tomcat 的 JAR 运行 Spring Boot 应用程序。但是,当我将相同的 JAR/项目上传到 CloudFoundry 或 OpenShift(并将其作为可执行 JAR 运行)时,建立 WebSocket 连接时的协议升级失败。

我制作了一个小示例项目来演示这个问题(至少当我在我的机器或我的 CloudFoundry 或 OpenShift 帐户上尝试它时)。可在此处获得:https://github.com/shakuzen/spring-stomp-websocket-test

这是一个精简的、简单的示例,但我能够始终如一地重现该问题。日志中的错误信息是:

2014-10-20T00:46:36.69+0900 [App/0]   OUT 2014-10-19 15:46:36.698 DEBUG 32 --- [io-61088-exec-5] o.s.w.s.s.s.DefaultHandshakeHandler      : Invalid Upgrade header null

在此之前 DefaultHandshakeHandler 的调试日志显示升级标头丢失。但是,如果您查看使用 Chrome 的开发人员工具(或任何浏览器的等效工具)发送的请求,您会发现请求有所不同。发送了以下两个请求。

1

GET /hello/info HTTP/1.1
Host: sswss-test.cfapps.io
Connection: keep-alive
Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.104 Safari/537.36
Accept: */*
Referer: http://sswss-test.cfapps.io/message
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,ja;q=0.6
Cookie: __VCAP_ID__=693dd6ff1b494f88a2c8567590da500dc44b4818746a45b28dd98a29b2607395; JSESSIONID=C5485065FE0A1DBCDF1F148A63D08FC2
DNT: 1

2

GET ws://sswss-test.cfapps.io/hello/863/olm1kojs/websocket HTTP/1.1
Host: sswss-test.cfapps.io
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Authorization: Basic dGVzdHVzZXI6dGVzdHBhc3M=
Upgrade: websocket
Origin: http://sswss-test.cfapps.io
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.104 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,ja;q=0.6
Cookie: __VCAP_ID__=693dd6ff1b494f88a2c8567590da500dc44b4818746a45b28dd98a29b2607395; JSESSIONID=C5485065FE0A1DBCDF1F148A63D08FC2
Sec-WebSocket-Key: 7bW1pg6f9axkVfqV21k/9w==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits

似乎它正在获取第一个请求的标头并因此而失败。但是,当它在我的本地计算机上运行并且没有此问题时,发送相同的两个请求(当然使用 localhost 而不是 sswss-test.cf.apps.io 和不同的安全标头值)。我在 Chrome 和 Firefox 上试过这个。

我链接的 GitHub 项目使用的是 Spring Boot 1.2.0.M2,但我也使用最新发布版本 (1.1.8.RELEASE) 进行了测试,得到了相同的结果。我也在搜索中发现,也许 SockJS 不能很好地处理相对 URL,所以我尝试从控制台运行带有绝对 URL 的连接命令:

var socket2 = new SockJS('http://sswss-test.cfapps.io/hello');

但不幸的是结果是一样的。

非常感谢任何建议或解决方案。随意 fork GitHub 项目并搞砸它(OpenShift 有一个免费帐户选项,因此您可以免费部署在那里重新创建问题)。我将在此处复制项目的相关部分。

WebSocket配置

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

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

安全配置

我的实际应用程序使用 Facebook 进行身份验证,但我能够通过基本身份验证重现该问题,因此我不想再浪费时间进行额外设置和增加复杂性。

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .httpBasic()
            .and()
            //Configures url based authorization
            .authorizeRequests()
                // Anyone can access the urls
                .antMatchers("/").permitAll()
                //The rest of the our application is protected.
                .antMatchers("/**").authenticated();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .inMemoryAuthentication()
                .withUser("testuser").password("testpass").roles("USER").and()
                .withUser("adminuser").password("adminpass").roles("ADMIN","USER");
    }
}

【问题讨论】:

  • 那么运气好吗?我也想解决它
  • 我很抱歉花了这么长时间才回复。我一直忙于工作和搬家,我没有机会写一个适当的回应。对我来说,问题是端口。 OpenShift 和 CloudFoundry 只允许特定端口上的 websocket 连接。这就是为什么它可以在没有此类限制的本地计算机上运行的原因。只需将特定端口添加到 URL 即可让事情正常工作,但会使 URL 变得丑陋。如果可能的话,这是我想要找到解决方案的下一件事。在接下来的几天里,我会尝试用链接写一个正确的答案。
  • @TommyLudwig 啊哈!这正是 Stack Overflow 希望帮助消除的东西——你有问题,你搜索再搜索,找到有同样问题的人。 4 年前,他们在论坛帖子上突然出现并说:“没关系,我想通了。”你发现了什么,伙计?在这里帮助我们。

标签: spring-security spring-boot stomp sockjs spring-websocket


【解决方案1】:

问题是 OpenShift 的端口限制,当时他们支持 websocket。请参阅他们的博客文章https://blog.openshift.com/paas-websockets/,其中提到:

因此,对于普通的 WebSockets ws://,您将使用端口 8000,对于安全连接,您将使用 wss:// 端口 8443。

当我打开 SPR-12371 时,来自 Spring Framework 团队的 Rossen 发现了这一点。

Stack Overflow 上还有另一个答案相同的答案:https://stackoverflow.com/a/19952072

【讨论】:

    猜你喜欢
    • 2021-03-11
    • 2015-09-21
    • 2021-08-09
    • 2013-06-02
    • 2017-01-19
    • 2017-02-16
    • 2019-07-25
    • 2020-01-07
    • 1970-01-01
    相关资源
    最近更新 更多