Closed tjmeal closed 1 year ago
Hi,
i am not sure what might be the issue from the code you provided. But please note that webSocketConnectHeaders
are not supported in web. I suggest you move your authorization to the stompConnectHeaders
.
The difference between webSocketConnectHeaders
and stompConnectHeaders
is where they are used. The former are used when the WebSocket connection is being established, while the latter are being used when the STOMP protocol does its connect handshake (this is not defined in the spec and needs some custom code in Spring)
Hello and thanks for your reply,
So since i need webSocketConnectHeaders to establish the connection and its not supported in web there is no other way right? i had tried stompHeaders but the connection can't get established Is the any planning for webSocketConnectHeaders to be supported on web too ?
Hey,
this is not a restriction of this library, but a restriction of WebSockets in the JavaScript WebSockets API. If you want to use stompConnectHeaders you need to have some additional code on your server, i.e. something like this (in your WebSocketMessageBrokerConfigurer):
@Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
@Override
public Message<?> preSend(@NotNull Message<?> message, @NotNull MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor != null ) {
if (SimpMessageType.CONNECT.equals(accessor.getMessageType())) {
Optional.ofNullable(accessor.getNativeHeader("Authorization")).ifPresent(ah -> {
String bearerToken = ah.get(0).replace("Bearer ", "");
try {
Authentication authentication = authService.getAuthentication(bearerToken);
accessor.setUser(authentication);
} catch (Exception e) {
}
});
}
}
return message;
}
});
}
Hey again,
I would like for starters to thank you for effort in any case !!
But i am not very familiar with the internals, but with few i know.
First i need to make a connection (http call to connect to the channel) and then send stompHeaders.
If i remove websocketHeaders and i am using only stompHeader > i am getting the following error:
Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is org.springframework.security.access.AccessDeniedException: Access is denied
from my public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer.
By your suggestion to use only using stompHeaders > if i understood correctly i should leave open the web-socket connection and authenticate on configureClientInboundChannel ?
If thats what you mean is that even secure ?
Thanks again and i will be waiting your reply.
Hey,
yes my suggestion would be to use stompConnectHeaders
and implement a custom interceptor, which bascially is inspecting the headers of every CONNECT
message send by the Stomp protocol. If a authorization header is present it uses it to authorize this connect request, if not it throws an exception (my example above catches it, but you would need to throw one). That way the stomp connection is only established if it has a Bearer token.
Regarding security: Yes. This should be secure, as long as their is no other operation possible on the active WebSocket connection apart from using it for the Stomp protocol.
Hello and i can't thank you enough for your help.
One last connection here, and i am asking because it looks you have been there already :)
Is the a way on which i can disable any other operation on the active Web-socket connection except stomp protocol ?
Thanks again.
Is the a way on which i can disable any other operation on the active Web-socket connection except stomp protocol ?
As far as i know there is no action possible, if you don't provide any Controllers working on the raw Websocket. The only security concern could maybe be that someone is able to open many WebSocket connections and effectively DDoS your server, but the same is true if you do Authorization on the WebSocket.
Same stance for authorization of http requests too then, So you probably right.
Thanks again for you time !! Have a great day.
Hello and sorry for bothering you again.
I find an issue maybe you have encounter that yourself, the code you sended to me in order to use stompHeader for Auth it connect successfully but on subscribe the user is null on the accessor.
@SubscribeMapping("/user/queue/transfers")
public WsTransferResponse subscribe() {
return WsTransferResponse.builder()
.transfers(transferFacade.findAllTransfersToOrganize())
.action(WsActionEnum.SUBSCRIBE)
.build();
}
I even changed the code to include Subscription but still the user is not in the context > i read something that @SubscribeMapping skips messageBroker does that have something to do with it ?
` @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors(new ChannelInterceptor() { @Override public Message<?> preSend(@Nonnull Message<?> message, @Nonnull MessageChannel channel) { StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (accessor != null) {
if (SimpMessageType.CONNECT.equals(accessor.getMessageType()) || SimpMessageType.SUBSCRIBE.equals(accessor.getMessageType())) {
Optional.ofNullable(accessor.getNativeHeader("Authorization")).ifPresent(ah -> {
String bearerToken = ah.get(0).replace("Bearer ", "");
try {
if (StringUtils.hasText(bearerToken) && tokenProvider.validateToken(bearerToken)) {
Authentication authentication = tokenProvider.getAuthentication(bearerToken);
accessor.setUser(authentication);
}
} catch (Exception e) {
throw new CustomException("Error With Stomp Connection");
}
});
}
}
return message;
}
});
}`
Hey,
in your config that extends AbstractSecurityWebSocketMessageBrokerConfigurer
, did you configure you configureInbound
? Something like:
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
messages
.simpTypeMatchers(CONNECT, UNSUBSCRIBE, DISCONNECT, HEARTBEAT).permitAll()
.simpDestMatchers("/topic/public/**", "/queue/public/**").permitAll()
.simpDestMatchers("/**").authenticated()
.anyMessage().denyAll();
}
Nop i had delete completely that class 👯 Do you have any idea why that happened only on Subscription was indeed about @SubscribeMapping ?
Now works btw, i will dig in to find a solution to have and when i do i'll let you know that the least i can do :D
I found "A common pattern for achieving WebSocket authentication/authorization is to implement a ticketing system where the page hosting the WebSocket client requests a ticket from the server and then passes this ticket during WebSocket connection setup either in the URL/query string, in the protocol field, or required as the first message after the connection is established. "
from here https://stackoverflow.com/questions/4361173/http-headers-in-websockets-client-api. but no idea how to implement it yet :P
Hello,
I do have an implementation which works on ios/android but when running it on web i get access denied error
"<<< a["ERROR\nmessage:Failed to send message to ExecutorSubscribableChannel[clientInboundChannel]; nested exception is org.springframework.security.access.AccessDeniedException\c Access is denied\ncontent-length:0\n\n\u0000"]"
Bellow is my implementation :
final stompClient = StompClient( config: StompConfig.SockJS( url: '$BASE_ROUTE_ENDPOINT/websocket', onConnect: onConnect, webSocketConnectHeaders: {'Authorization': 'Bearer $_jwtToken'},
this in my case looks obsolete: Where it is used ? stompConnectHeaders: {'Authorization': 'Bearer $_jwtToken'},
), );
Thanks in advance and i will be waiting your reply with any thoughts, because i have no idea what might be the problem...