Защищенное обновление WebSocket через STOMP через SockJS завершается с ошибкой с нулевым заголовком Invalid Upgrade.

Я работаю над веб-приложением, которое использует Spring Security и WebSockets. Я могу без проблем использовать WebSockets с моей локальной машины, запуская приложение Spring Boot из JAR со встроенным Tomcat. Однако, когда я загружаю тот же 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

Журналы DEBUG для DefaultHandshakeHandler до этого показывают, что заголовок Upgrade отсутствует. Однако если вы посмотрите на запрос, отправленный с помощью инструментов разработчика 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

Кажется, он берет заголовки первого запроса и из-за этого терпит неудачу. Однако те же два запроса (конечно, с локальным хостом вместо 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');

Но, к сожалению, результат был тот же.

Любые предложения или решения приветствуются. Не стесняйтесь разветвлять проект GitHub и возиться с ним (у OpenShift есть опция бесплатной учетной записи, поэтому вы можете бесплатно развернуть там, чтобы воссоздать проблему). Я скопирую соответствующие части проекта здесь.

WebSocketConfig

@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();
    }
}

SecurityConfig

Мое фактическое приложение использует 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");
    }
}

person Tommy Ludwig    schedule 19.10.2014    source источник
comment
так что повезло с этим? Я также хочу решить это   -  person naoru    schedule 25.10.2014
comment
Прошу прощения, что так долго не отвечал. Я был занят работой и переездом, поэтому у меня не было возможности написать надлежащий ответ. Для меня проблема была в портах. OpenShift и CloudFoundry разрешают подключения через веб-сокеты только к определенным портам. Вот почему он работает на моей локальной машине, на которой нет таких ограничений. Простое добавление определенного порта к URL-адресу позволяет работать, но затем делает URL-адрес уродливым. Это следующая вещь, для которой я хочу найти решение, если это возможно. Я постараюсь написать правильный ответ со ссылками в ближайшие несколько дней.   -  person Tommy Ludwig    schedule 05.11.2014
comment
@TommyLudwig Аааа! Это именно то, что Stack Overflow надеялся помочь устранить — у вас есть проблема, вы ищете и ищете, находите кого-то с такой же проблемой. Они заглядывают в ветку форума 4-летней давности и говорят, неважно, я разобрался. Что ты понял, чувак? Помогите нам здесь.   -  person Chris Baker    schedule 18.02.2019


Ответы (1)


Проблема заключалась в ограничениях портов на OpenShift с их поддержкой веб-сокетов в то время. См. их сообщение в блоге https://blog.openshift.com/paas-websockets/, которое упоминает:

Итак, для простых WebSockets ws:// вы будете использовать порт 8000, а для защищенных подключений wss:// порт 8443.

Россен из команды Spring Framework понял это, когда я открыл SPR-12371.

В Stack Overflow есть еще один ответ с тем же ответом: https://stackoverflow.com/a/19952072

person Tommy Ludwig    schedule 19.02.2019