1. введение
В предыдущей статье мы показали , как добавить WebSockets в
Здесь мы опишем, как добавить безопасность в Spring WebSockets в Spring MVC . Прежде чем продолжить, убедитесь, что у вас уже есть базовое покрытие безопасности Spring MVC – если нет, ознакомьтесь с этой статьей .
2. Зависимости Maven
Есть две основные группы зависимостей Maven , которые нам нужны для нашей реализации WebSocket.
Во-первых, давайте определим общие версии Spring Framework и Spring Security, которые мы будем использовать:
5.2.8.RELEASE 5.2.3.RELEASE
Во-вторых, давайте добавим основные библиотеки Spring MVC и Spring Security, необходимые для реализации базовой аутентификации и авторизации:
org.springframework spring-core ${spring.version} org.springframework spring-web ${spring.version} org.springframework spring-webmvc ${spring.version} org.springframework.security spring-security-web ${spring-security.version} org.springframework.security spring-security-config ${spring-security.version}
Последние версии spring-core , spring-web , spring-webmvc , spring-security-web , spring-security-config можно найти на Maven Central.
Наконец, давайте добавим необходимые зависимости:
org.springframework spring-websocket ${spring.version} org.springframework spring-messaging ${spring.version} org.springframework.security spring-security-messaging ${spring-security.version}
Вы можете найти последнюю версию spring-websocket , spring-messaging и spring-security-messaging на Maven Central.
3. Базовая безопасность WebSocket
Безопасность, специфичная для WebSocket, с использованием spring-security-messaging библиотека сосредоточена на AbstractSecurityWebSocketMessageBrokerConfigurer классе и его реализации в вашем проекте:
@Configuration public class SocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer { //... }
AbstractSecurityWebSocketMessageBrokerConfigurer класс обеспечивает дополнительное покрытие безопасности , предоставляемое WebSecurityConfigurerAdapter.
Библиотека spring-security-messaging – не единственный способ реализации безопасности для веб-сайтов. Если мы придерживаемся обычной библиотеки spring-websocket , мы можем реализовать интерфейс WebSocketConfigurer и прикрепить перехватчики безопасности к нашим обработчикам сокетов.
Поскольку мы используем библиотеку spring-security-messaging , мы будем использовать подход AbstractSecurityWebSocketMessageBrokerConfigurer|/.
3.1. Реализация настройки входящих()
Реализация configure Inbound() является наиболее важным шагом в настройке вашего AbstractSecurityWebSocketMessageBrokerConfigurer подкласса:
@Override protected void configureInbound( MessageSecurityMetadataSourceRegistry messages) { messages .simpDestMatchers("/secured/**").authenticated() .anyMessage().authenticated(); }
В то время как WebSecurityConfigurerAdapter позволяет указать различные требования к авторизации для различных маршрутов в рамках всего приложения, AbstractSecurityWebSocketMessageBrokerConfigurer позволяет указать конкретные требования к авторизации для назначений сокетов.
3.2. Соответствие типа и назначения
Реестр сообщений SecurityMetadataSource позволяет нам указывать ограничения безопасности, такие как пути, роли пользователей и разрешенные сообщения.
Сопоставители типов ограничивают, какие SimpMessageType разрешены и каким образом :
.simpTypeMatchers(CONNECT, UNSUBSCRIBE, DISCONNECT).permitAll()
Назначение соответствует ограничению, какие шаблоны конечных точек доступны и каким образом :
.simpDestMatchers("/app/**").hasRole("ADMIN")
Subscribe destination matchers map a List of Simple Destination MessageMatcher i экземпляры , которые совпадают с SimpMessageType.SUBSCRIBE:
.simpSubscribeDestMatchers("/topic/**").authenticated()
Вот полный список всех доступных методов для сопоставления типа и назначения .
4. Обеспечение безопасности Маршрутов Сокетов
Теперь, когда мы познакомились с базовой безопасностью сокетов и конфигурацией соответствия типов, мы можем объединить безопасность сокетов, представления, STOMP (протокол обмена текстовыми сообщениями), брокеры сообщений и контроллеры сокетов, чтобы обеспечить безопасность WebSockets в нашем приложении Spring MVC.
Во-первых, давайте настроим наши представления сокетов и контроллеры для базового покрытия безопасности Spring:
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) @EnableWebSecurity @ComponentScan("com.baeldung.springsecuredsockets") public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/index", "/authenticate").permitAll() .antMatchers( "/secured/**/**", "/secured/success", "/secured/socket", "/secured/success").authenticated() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login").permitAll() .usernameParameter("username") .passwordParameter("password") .loginProcessingUrl("/authenticate") //... } }
Во-вторых, давайте настроим фактическое место назначения сообщения с требованиями аутентификации:
@Configuration public class SocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer { @Override protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { messages .simpDestMatchers("/secured/**").authenticated() .anyMessage().authenticated(); } }
Теперь в нашем WebSocketMessageBrokerConfigurer мы можем зарегистрировать фактическое сообщение и топнуть конечные точки:
@Configuration @EnableWebSocketMessageBroker public class SocketBrokerConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/secured/history"); config.setApplicationDestinationPrefixes("/spring-security-mvc-socket"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/secured/chat") .withSockJS(); } }
Давайте определим пример контроллера сокета и конечную точку, для которой мы обеспечили покрытие безопасности выше:
@Controller public class SocketController { @MessageMapping("/secured/chat") @SendTo("/secured/history") public OutputMessage send(Message msg) throws Exception { return new OutputMessage( msg.getFrom(), msg.getText(), new SimpleDateFormat("HH:mm").format(new Date())); } }
5. Политика Того Же Происхождения
Политика Same Origin требует, чтобы все взаимодействия с конечной точкой происходили из того же домена, в котором было инициировано взаимодействие.
Например, предположим, что ваша реализация WebSockets размещена по адресу foo.com , и вы применяете ту же политику происхождения . Если пользователь подключается к вашему клиенту, размещенному на foo.com а затем открывает другой браузер для bar.com , затем bar.com не будет иметь доступа к вашей реализации WebSocket.
5.1. Переопределение одной и той же политики происхождения
Spring WebSockets применяет ту же политику происхождения из коробки, в то время как обычные WebSockets этого не делают.
На самом деле, Spring Security требует маркера CSRF ( Подделка межсайтового запроса ) для любого допустимого ПОДКЛЮЧЕНИЯ типа сообщения:
@Controller public class CsrfTokenController { @GetMapping("/csrf") public @ResponseBody String getCsrfToken(HttpServletRequest request) { CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class.getName()); return csrf.getToken(); } }
Вызывая конечную точку по адресу /csrf , клиент может получить токен и пройти аутентификацию через уровень безопасности CSRF.
Однако Ту же политику происхождения для Spring можно переопределить , добавив следующую конфигурацию в AbstractSecurityWebSocketMessageBrokerConfigurer :
@Override protected boolean sameOriginDisabled() { return true; }
5.2. Поддержка STOMP, SockJS и параметры фрейма
Обычно используется STOMP вместе с SockJS для реализации поддержки Spring WebSockets на стороне клиента.
SockJS по умолчанию настроен на запрет переноса через элементы HTML iframe . Это делается для предотвращения угрозы взлома .
Однако существуют определенные случаи использования, когда разрешение iframes использовать транспорт SockJS может быть полезным. Для этого вы можете переопределить конфигурацию по умолчанию в WebSecurityConfigurerAdapter :
@Override protected void configure(HttpSecurity http) throws Exception { http .csrf() //... .and() .headers() .frameOptions().sameOrigin() .and() .authorizeRequests(); }
Обратите внимание, что в этом примере мы следуем Той же политике происхождения , несмотря на разрешение переноса через iframes .
6. Покрытие Oauth2
Специфичная для Oauth2 поддержка Spring WebSockets становится возможной благодаря реализации покрытия безопасности Oauth2 в дополнение к вашему стандарту WebSecurityConfigurerAdapter coverage и расширению его . Вот пример того, как реализовать Oauth2.
Для аутентификации и получения доступа к конечной точке WebSocket вы можете передать Oauth2 access_token в параметр запроса при подключении от клиента к серверному WebSocket.
Вот пример, демонстрирующий эту концепцию с использованием SockJS и STOMP:
var endpoint = '/ws/?access_token=' + auth.access_token; var socket = new SockJS(endpoint); var stompClient = Stomp.over(socket);
7. Заключение
В этом кратком руководстве мы показали, как добавить безопасность в Spring WebSockets. Взгляните на справочную документацию Spring WebSocket и WebSocket Security , чтобы узнать больше об этой интеграции.
Как всегда, проверьте наш проект Github для примеров, используемых в этой статье.