Add Auth token for spring web socket - java

I'm implementing a simple socket like this :
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + message.getName() + "!");
}
From the client-side :
function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
And the websocket configuration
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
Now I would like to apply authentication for the socket so that not all clients can connect to server. I may look like
var socket = new SockJS('/gs-guide-websocket?token= a JWT token'); //or sth similar
from the client-side.
Let just assume that I can hide the token from people who inspect the frontend code, how do I get that token and verify it from the server side ? (Provided that I have the function to verify the JWT already)
Or is there any better way of implementing security for socket connection that you can suggest ?

Related

spring stomp websocket + ActiveMQ [duplicate]

This question already has an answer here:
Spring websocket end-poind and send message
(1 answer)
Closed 7 months ago.
I'm trying to build a Spring STOMP websocket + ActiveMQ service. I have set the websocket and the ActiveMQ queue.
ActiveMQ queue works just fine but I'm not able to make my websocket endpoint send messages to the clients connected to the topic.
Websocket client seems to connect just fine also. The thing is that when the controller receives information it is not caught on the client.
--WebsocketConfig.java--
#Configuration
#EnableWebSocketMessageBroker
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
}
--WebsocketController.java--
#Controller
public class WebsocketController {
#Autowired
private ItemService itemService;
#JmsListener(destination = "items-queue")
#MessageMapping("/websocket")
#SendTo("/topic/items")
public String itemsWebsocket(Iterable<Item> items) {
System.out.println("Websocket controller reached");
for (Item item : items) System.out.println(item.getName());
return "hi from websocket";
}
}
--app.js--
let stompClient = null;
function connect() {
let socket = new SockJS('/websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/items', function (items) {
appendItems(items);
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
console.log("Disconnected");
}
function appendItems(items) {
console.log(items);
const itemListContainer = document.getElementById("item-list");
itemListContainer.innerText = "";
Array.from(items).forEach( item => {
const itemContainer = document.createElement("div");
itemContainer.innerText = item.name;
itemListContainer.append(itemContainer);
});
}
connect();
I just found out that if I apply the changes from here it does the thing.

websocket gets blocked by CORS policy: No 'Access-Control-Allow-Origin' spring boot

I am trying to run a websocket example having java code in one port(8080) and java script code in another port(8080)
problem : when i deploy client and server on same port it works properly but when client app.js is on different port than server port it throws error
Access to XMLHttpRequest at
'http://localhost:8091/websocket-example/info?t=1601539296700' from
origin 'http://localhost:8080' has been blocked by CORS policy: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
java configuration file
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/websocket-example")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic");
registry.setApplicationDestinationPrefixes("/app");
}
}
contoller file
#Controller
public class ChattingController {
#MessageMapping("/user")
#SendTo("/topic/user")
public UserResponse getUser(User user) {
return new UserResponse("Hi " + user.getName());
}
}
app.js
var stompClient = null;
function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
}
else {
$("#conversation").hide();
}
$("#userinfo").html("");
}
function connect() {
var socket = new SockJS('http://localhost:8091/websocket-example');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/user', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
function disconnect() {
if (stompClient !== null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}
function sendName() {
stompClient.send("/app/user", {}, JSON.stringify({'name': $("#name").val()}));
}
function showGreeting(message) {
$("#userinfo").append("<tr><td>" + message + "</td></tr>");
}
$(function () {
$("form").on('submit', function (e) {
e.preventDefault();
});
$( "#connect" ).click(function() { connect(); });
$( "#disconnect" ).click(function() { disconnect(); });
$( "#send" ).click(function() { sendName(); });
});
springsecurity file
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer, WebMvcRegistrations {
private static final String EVERYTHING = "/**";
#Bean
public SuperInterceptor superInterceptor() {
return new SuperInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
List<String> paths = new ArrayList<String>();
paths.add("/login");
paths.add("/user/register");
registry.addInterceptor(superInterceptor()).addPathPatterns(EVERYTHING).excludePathPatterns(paths);
}
#Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity.csrf().disable();
httpSecurity.cors();
}
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("*");
}
}
ps: i am new to websocket sorry if this is a naive issue
If you configure it with Spring Security then try this, it might helps you. Add it in the WebSecurityConfig file of Spring Security.
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource(request -> new CorsConfiguration().applyPermitDefaultValues());
}
OR
If you are just configure the Spring MVC then try this:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST","PUT", "DELETE");
}
};
}

Spring Boot WebSocket - how to get notified on client subscriptions

I have an application with a large number of groups, where my server is using a message queue (RabbitMQ) to observe the groups and post notification to the user upon changes over WebSocket. I'm using Spring boot and their WebSocket implementation inspired by this guide: https://spring.io/guides/gs/messaging-stomp-websocket/
Here is an example of the JavaScript client subscribing to the channel:
var socket = new SockJS('http://localhost/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/group/1/notification', function (message) {
// to something..
});
});
My Java Spring WebSocket controller has this broadcastNotification method sending messages to the /topic/group/{groupId}/notification channel.
#Controller
public class GroupController {
private SimpMessagingTemplate template;
#Autowired
public GroupController(SimpMessagingTemplate template) {
this.template = template;
}
public void broadcastNotification(int groupId, Notification notification) {
this.template.convertAndSend("/topic/group/." + tenantId + "/notification", Notification);
}
}
Thats working fine, but with performacne in mind I would like my to business logic to only observe groups currently beeing subscribed on WebSocket.
How can I be notified on my server when clients subscribe to the /topic/group/1/notification or /topic/group/1/* channel? The web users will be subscribing and unsubscribing as they browse the web page.
You can listen to the event SessionSubscribeEvent like this:
#Component
public class WebSocketEventListener {
#EventListener
public void handleSessionSubscribeEvent(SessionSubscribeEvent event) {
GenericMessage message = (GenericMessage) event.getMessage();
String simpDestination = (String) message.getHeaders().get("simpDestination");
if (simpDestination.startsWith("/topic/group/1")) {
// do stuff
}
}
}
You can use annotation-driven event listener (Kotlin code):
#EventListener
private fun onSubscribeEvent(event: SessionSubscribeEvent) {
// do stuff...
}
Such event listeners can be registered on any public method of a managed bean via the #EventListener annotation.
Spring websockets events docs
Spring events examples
You can detect when a client subscribes to a topic using interceptors in the WebSocketConfig class:
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration){
registration.interceptors(new ChannelInterceptor() {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if(StompCommand.CONNECT.equals(accessor.getCommand())){
System.out.println("Connect ");
} else if(StompCommand.SUBSCRIBE.equals(accessor.getCommand())){
System.out.println("Subscribe ");
} else if(StompCommand.SEND.equals(accessor.getCommand())){
System.out.println("Send message " );
} else if(StompCommand.DISCONNECT.equals(accessor.getCommand())){
System.out.println("Exit ");
} else {
}
return message;
}
});
}
}
The accessor object contains all information sent from the client.

Spring session + Spring web socket. Send message to specific client based on session id

I have followed Quetion1 and Quetion2 from stack overflow to send messages to specific client, based on its sessionId but could not find success.
Below is my sample RestController class
#RestController
public class SpringSessionTestApi {
#Autowired
public SimpMessageSendingOperations messagingTemplate;
#MessageMapping("/messages")
public void greeting(HelloMessage message, SimpMessageHeaderAccessor headerAccessor) throws Exception {
String sessionId = (String) headerAccessor.getSessionAttributes().get("SPRING.SESSION.ID");
messagingTemplate.convertAndSendToUser(sessionId,"/queue/test",message, createHeaders(sessionId));
}
private MessageHeaders createHeaders(String sessionId) {
SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
return headerAccessor.getMessageHeaders();
}
}
Session Id: when client sends createSession request, new spring sessionId is generated and same is stored in MongoDB as well. After that when client sends web socket connect request, same sessionId is received which was stored in mongoDb as expected. Till This everything is working fine.
Now my job is to send response back to the client based on the sessionId.
For that I have below web socket class:
#Configuration
#EnableScheduling
#EnableWebSocketMessageBroker
public class WebSocketConfig extends
AbstractSessionWebSocketMessageBrokerConfigurer<ExpiringSession> {
#Override
protected void configureStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messages");
}
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue");
registry.setApplicationDestinationPrefixes("/app");
}
}
and the sample client code that I am using to connect is:
function connect() {
stompClient = Stomp.client('ws://localhost:8016/messages');
stompClient.debug = null;
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/user/queue/test', function (greeting) {
console.log("Hello "+greeting);
console.log("Greeting body "+JSON.parse(greeting.body));
});
});
}
Please help, Where I am doing wrong in this?
Thanks in Advance!
If you are using /user channel as you do, try to pass the user as stated here.
#MessageMapping("/messages")
public void greeting(HelloMessage message, SimpMessageHeaderAccessor headerAccessor, Principal principal)
throws Exception {
messagingTemplate.convertAndSendToUser(principal.getName(), "/queue/test", message);
}
I've found a full workable Spring Stomp Chat project in git, the link is here. You can refer to it.
https://gist.github.com/theotherian/9906304

Get data from a web socket in a Spring Boot web application

I am new to both Spring Boot and Websockets.
I am comfortable with Java and have read a few things on Websockets and Spring Boot framework.
I need to communicate with a Web socket and get the data in an existing Spring Boot web app.
Can anyone let me know where I can start and any good online resources ? I did Google, but most of the examples are difficult for me to grasp in a short time. If possible, also explain it to me conceptually.
Thanks in advance
To create a basic websockeet applicaiton in spring boot
You will need to:
- First, Enable Websocket support, For example by using:
#Controller
public class GreetingController {
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000); // simulated delay
return new Greeting("Hello, " + message.getName() + "!");
}
}
- Second, Create a message-handling controller:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket").withSockJS();
}
}
- Create a browser client
function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
I think you can start with this example, https://spring.io/guides/gs/messaging-stomp-websocket/
Check out my answer to a similar question:
SockJS Java Client Implementation for non-web application
Within, you can find an example of a client able to send/receive websocket data.

Categories

Resources