Reputation: 451
I'm developing an application which require WebSocket connections. I have developed a Spring Boot microservice called notification-service
which holds web socket configurations. Further, I have exposed all the notification-service
APIs via gateway-service
(exposed via port 8747) with integrated with Spring Security.
I have the following code to test the WebSocket connection.
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.6.1/sockjs.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.js"></script>
<script type="text/javascript">
var stompClient = null;
const LOCAL_URL = 'http://localhost:8747/websocket/notification-service/ws';
var socket = new SockJS(LOCAL_URL);
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log(frame);
stompClient.subscribe('/topic/notification', function(result) {
show(JSON.parse(result.body));
});
});
function sendMessage() {
var text = document.getElementById('text').value;
stompClient.send("/app/application", {},
JSON.stringify({'text':text}));
}
function show(message) {
var response = document.getElementById('messages');
var p = document.createElement('p');
p.innerHTML= "message: " + message.message;
response.appendChild(p);
}
</script>
</head>
<body>
<div>
<div>
<button id="sendMessage" onclick="sendMessage();">Send</button>
<input type="text" id="text" placeholder="Text"/>
</div>
<br />
<div>
<button id="sendPrivateMessage" onclick="sendPrivateMessage();">Send Private</button>
<input type="text" id="privateText" placeholder="Private Message"/>
<input type="text" id="to" placeholder="To"/>
</div>
<br />
<br />
<br />
<div id="messages"></div>
</div>
</body>
</html>
Please also find following, Spring Security configurations and response filter which I have configured in gateway-service
. I have whitelisted all endpoints related to the web sockets as well.
Spring Security configuration
@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {
private static final String[] AUTH_WHITE_LIST = {
"/websocket/sds-notification-service/**"
};
@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
return http
.csrf(ServerHttpSecurity.CsrfSpec::disable)
.authorizeExchange(exchanges -> exchanges.pathMatchers(AUTH_WHITE_LIST)
.permitAll()
.anyExchange().authenticated())
.build();
}
}
Post Filter
@Configuration
public class GlobalResponseFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
var response = exchange.getResponse();
var request = exchange.getRequest();
response.getHeaders().set("Access-Control-Allow-Credentials", "true");
response.getHeaders().set("Access-Control-Allow-Origin", request.getHeaders().getOrigin());
exchange.mutate().response(response).build();
}));
}
@Override
public int getOrder() {
return 0;
}
}
I'm unable to establish the WebSocket connection as I'm getting the following error in the console.
Update
I do have another authorization filter (which I forgot to mentioned earlier) as for the following code to perform some permission validation. And I remove that filter, I was able to connect to the WebSocket as expected.
@Component
public class AuthorizationFilter extends AbstractGatewayFilterFactory<AuthorizationFilter.Config> {
public AuthorizationFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> ReactiveSecurityContextHolder.getContext()
.map(securityContext -> {
if (someValidateLogic()) {
return chain.filter(exchange);
} else {
return Mono.error(new AuthorizationServiceException(HttpStatus.FORBIDDEN.toString()));
}
})
.flatMap(classMono -> classMono)
.then());
}
}
I'm not sure whether this due to a invalid filter order which we have configured.
Upvotes: 2
Views: 61