If you have a Spring Boot application, that already uses Spring Security, it can be tricky to set up the security for Websocket communication. The problem is, that in case of you need to protect your resources with basic authentication, it avoids the connection to your Websocket service, while Websocket can not work together with basic authentication.
The secret is to enable basic authentication only for targeted endpoints, and define a second configuration for Websocket connections.
In the example below, I define basic authentication for some Actuator endpoints, and allow to reach other resources without authentication. Additionally I set the Websocket security with my own configuration class.
@EnableWebSecurity public class SecurityConfig { @Configuration @Order(1) public static class ActuatorWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { private static final String ACTUATOR = "ACTUATOR"; // user for Actuator endpoints, defined in properties file @Value("${actuator.user}") private String actuatorUser; // password for Actuator endpoints, defined in properties file @Value("${actuator.password}") private String actuatorPassword; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .passwordEncoder(passwordEncoder()) .withUser(actuatorUser).password(passwordEncoder().encode(actuatorPassword)) .authorities("ROLE_ACTUATOR"); } @Bean public static PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/backendVersionWithAuth").hasRole(ACTUATOR) .requestMatchers(EndpointRequest.to(MetricsEndpoint.class)).hasRole(ACTUATOR) .requestMatchers(EndpointRequest.to(InfoEndpoint.class)).hasRole(ACTUATOR) .requestMatchers(EndpointRequest.to(HealthEndpoint.class)).permitAll() .and().httpBasic() .and().csrf().disable(); } } // define separate adapter for the remaining calls, while webSocket does not work with basic authentication @Configuration public static class RestWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .httpBasic().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests().antMatchers("/websocket*").permitAll() .and().csrf().disable(); } } }
As you can see, I enable the Actuator health endpoint for all everybody. Metrics and info endpoints can be read only with ACTUATOR role, after basic authentication, also with username and password, defined in the configuration.
For Websocket related requests, basic authentication is turned off, and connection allowed to everybody. The Websocket related authentication happens in my SecurityChannelInterceptor class, that implements org.springframework.messaging.support.ChannelInterceptor, and refuses unauthorized connections based on the information in connection message header, provided by the clients.
The basic Websocket related configuration is defined in the following class
@Configuration public class SocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer { @Override protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { // allow manage connection for all, without authentication messages .simpTypeMatchers(CONNECT, CONNECT_ACK, MESSAGE, SUBSCRIBE, UNSUBSCRIBE, HEARTBEAT, DISCONNECT, DISCONNECT_ACK, OTHER) .permitAll(); // allow sending messages for all, without authentication messages.anyMessage().anonymous(); } @Override protected boolean sameOriginDisabled() { return true; } }
As you can see, it allows all kind of messages to be sent, and again, the security interceptor does the real job of checking the authentication details. You can define multiple interceptors in order to combine rules, or restrict connections for some not basic cases.