I need to create a Websocket Server using spring which was easily completed by using the below code
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new SocketHandler(), "/poll");
registry.addHandler(new SocketPushHandler(), "/push");
}
}
Where SocketHandler and SocketPushHandler are the handler classes for the websocket endpoints.
Till this very thing is good able to run the server and connect to the endpoint using normal socket = new WebSocket(websocketUrl)
javascript code.
Now we need to have OAuth 2.0 Security implemented on the API endpoints which is done easily by importing some Spring security dependencies.\
Now the hard part is writing the client to connect the secure endpoint by passing the Oauth Authorization beaer <token>
as part of header.
From documents came to know that we can't send headers to Web-socket endpoints.
So according to the information form this Is it possible to secure WebSocket APIs with OAuth 2.0? link I created a API gateway /open-ws Request type GET
to which the client would connect and send the authorization headers and this endpoint internally on server-side will open an WebSocket Client connection passing the the headers as javax.websocket.WebSocketContainer
supports websocket client with custom headers.
So now my javascript first makes an GET ajax call to the Gateway endpoint and on success makes an new websocket request.
Below is the Spring API mimicking as gateway
@RequestMapping(value = "/open-ws", method = RequestMethod.GET)
public void getOperatorTokenDefinition(@RequestHeader(value = HttpHeaders.AUTHORIZATION) String bearerToken,
@RequestHeader(value = "websocketURL") String websocketURL,
HttpServletRequest acquireTokenServletRequest, HttpServletResponse response) {
webSocketClient.connecttoserver(websocketURL, acquireTokenServletRequest.getRemoteHost(), bearerToken, response);
// ResponseEntity<String> responseEntity = new ResponseEntity<>("connected", HttpStatus.OK);
}
}
Below is my Spring side client.
@Component
public class WebSocketClient {
private Session client;
public void connecttoserver(String websocketURL,String host,String bearerAccessToken, HttpServletResponse response) {
final AtomicReference<String> message = new AtomicReference<>();
Endpoint endpoint = new Endpoint() {
@Override
public void onOpen(Session session, EndpointConfig config) {
System.out.println("WSS OPEN!!!!!");
try (OutputStream output = response.getOutputStream()) {
output.write(session.getId());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
ClientEndpointConfig.Configurator configurator = new ClientEndpointConfig.Configurator() {
@Override
public void beforeRequest(Map<String, List<String>> headers) {
List<String> connection = new ArrayList<>(1);
connection.add("Upgrade");
List<String> origin = new ArrayList<>(1);
origin.add(originURL);
List<String> upgradeWebsocket = new ArrayList<>(1);
upgradeWebsocket.add("WebSocket");
List<String> host = new ArrayList<>(1);
host.add(websocketURL);
List<String> contenttype = new ArrayList<>(1);
contenttype.add("application/json");
List<String> authorization = new ArrayList<>(1);
authorization.add("Bearer " + bearerAccessToken);
List<String> tenantId = new ArrayList<>(1);
tenantId.add(tenantID);
List<String> key = new ArrayList<>(1);
key.add("HcFOxrSD89ya65X2qMF9lQ==");
List<String> version = new ArrayList<>(1);
version.add("13");
headers.put("Connection", connection);
headers.put("Upgrade", upgradeWebsocket);
headers.put("Host", host);
headers.put("Origin", origin);
// headers.put("Content-Type", contenttype);
headers.put("Authorization", authorization);
headers.put("Sec-WebSocket-Key", key);
headers.put("Sec-WebSocket-Version", version);
}
};
ClientEndpointConfig clientConfig = ClientEndpointConfig.Builder.create().configurator(configurator).build();
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
try {
// if (!this.client.isOpen())
this.client = container.connectToServer(endpoint, clientConfig, URI.create(websocketURL));
client.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String response) {
// TODO Auto-generated method stub
message.set(response);
// System.out.println("response>>>>>>>>>>>>>>>>>>> "+response.toString());// this dosent work
}
});
System.out.println("Response--------------------------------->" + message.get());
// client.close();
} catch (DeploymentException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Below is the JQuery code
$(document).ready(function(){
$("#Websocketconnect").click(function(){
$.ajax({
type: 'GET',
url: hostUrl.value,
headers: {
"Authorization":websocketToken.value,
"websocketURL":websocketUrl.value,
"Content-type":"text/plain"
},
success: function(result){
$("#readystatus").value = result;
webSocketCycleEvent(websocketUrl.value);
}});
});
});
function webSocketCycleEvent(websocketUrl){
socket = new WebSocket(websocketUrl);
socket.onerror = function(error) {
console.log('WebSocket Error: ' + error);
};
// Show a connected message when the WebSocket is opened.
socket.onopen = function(event) {
socketStatus.innerHTML = 'Connected to: ' + websocketUrl;
socketStatus.className = 'open';
};
socket.onmessage = function(event) {
var message = event.data;
messagesList.innerHTML += '<li class="received"><span>Received:</span>' +
message + '</li>';
};
socket.onclose = function(event) {
socketStatus.innerHTML = 'Disconnected from WebSocket.';
socketStatus.className = 'closed';
};
}
form.onsubmit = function(e) {
e.preventDefault();
// Retrieve the message from the textarea.
var message = messageField.value;
socket.send(message);
messagesList.innerHTML += '<li class="sent"><span>Sent:</span>' + message +
'</li>';
// Clear out the message field.
messageField.value = '';
return false;
};
I am not to connect to the websocket and send the socket.id as the server-side gives an
javax.websocket.DeploymentException: The HTTP response from the server [400] did not permit the HTTP upgrade to WebSocket
at org.apache.tomcat.websocket.WsWebSocketContainer.connectToServer(WsWebSocketContainer.java:343)
at com.example.simplewebsocketserver.WebSocketClient.connecttoserver(WebSocketClient.java:101)
at com.example.simplewebsocketserver.WebSocketController.getOperatorTokenDefinition(WebSocketController.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
So the Question arises is this the proper way of connecting the Oauth 2.0 Websocket, if yes how to deal with the above error, if no how to send headers to the authorization endpoint.
Note: No using Stomp because we are not yet confirmed whether the actual client i.e UI will allow/have stomp JS.