Фон
Я собираюсь создать веб-приложение RESTful с помощью Spring Boot (1.3.0.BUILD-SNAPSHOT), который включает в себя STOMP/SockJS WebSocket, который я намерен использовать в приложении iOS, а также в Интернете браузеры. Я хочу использовать JSON Web Tokens (JWT) для защиты запросов REST и интерфейса WebSocket, но Im имеет трудности с последним.
Приложение защищено с помощью Spring Безопасность: -
@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
public WebSecurityConfiguration() {
super(true);
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("steve").password("steve").roles("USER");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.exceptionHandling().and()
.anonymous().and()
.servletApi().and()
.headers().cacheControl().and().and()
// Relax CSRF on the WebSocket due to needing direct access from apps
.csrf().ignoringAntMatchers("/ws/**").and()
.authorizeRequests()
//allow anonymous resource requests
.antMatchers("/", "/index.html").permitAll()
.antMatchers("/resources/**").permitAll()
//allow anonymous POSTs to JWT
.antMatchers(HttpMethod.POST, "/rest/jwt/token").permitAll()
// Allow anonymous access to websocket
.antMatchers("/ws/**").permitAll()
//all other request need to be authenticated
.anyRequest().hasRole("USER").and()
// Custom authentication on requests to /rest/jwt/token
.addFilterBefore(new JWTLoginFilter("/rest/jwt/token", authenticationManagerBean()), UsernamePasswordAuthenticationFilter.class)
// Custom JWT based authentication
.addFilterBefore(new JWTTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
Конфигурация WebSocket является стандартной: -
@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
}
У меня также есть подкласс AbstractSecurityWebSocketMessageBrokerConfigurer
для защиты WebSocket: -
@Configuration
public class WebSocketSecurityConfiguration extends AbstractSecurityWebSocketMessageBrokerConfigurer {
@Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages.anyMessage().hasRole("USER");
}
@Override
protected boolean sameOriginDisabled() {
// We need to access this directly from apps, so can't do cross-site checks
return true;
}
}
Существует также несколько аннотированных классов @RestController
для обработки различных битов функциональности, и они успешно защищены через JWTTokenFilter
, зарегистрированный в моем классе WebSecurityConfiguration
.
Проблема
Однако я не могу заставить WebSocket быть защищенным с помощью JWT. Я использую SockJS 1.1.0 и STOMP 1.7.1 в браузере и не могу понять, как для передачи токена. Появится сообщение что SockJS не позволяет отправлять параметры с помощью начальных запросов /info
и/или рукопожатия.
Любое входящее сообщение CONNECT требует действительного токена CSRF для принудительной реализации той же политики происхождения
Что, по-видимому, подразумевает, что первоначальное рукопожатие должно быть необеспеченным и аутентификация, вызываемая в момент получения сообщения STOMP CONNECT. К сожалению, я не могу найти никакой информации в отношении ее реализации. Кроме того, для этого подхода потребуется дополнительная логика для отключения клиента-изгоя, который открывает соединение WebSocket и никогда не отправляет STOMP CONNECT.
Быть (очень) новым для Spring Я также не уверен, что и как Spring Сессии вписываются в это. Несмотря на то, что документация очень подробно, нет ничего приятного и простого (ака-идиота) руководства о том, как различные компоненты подходят друг другу/взаимодействуют друг с другом.
Вопрос
Как я могу обеспечить безопасность SockJS WebSocket, предоставляя токен JSON Web, желательно в момент рукопожатия (возможно ли это)?