Рубрики
Без рубрики

Введение в безопасность и веб-сайты

Краткое и практическое руководство о том, как добавить безопасность в WebSockets в приложении Spring MVC.

Автор оригинала: baeldung.

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 для примеров, используемых в этой статье.