CORS problems with Springboot and Angular Websocket - spring

I have two service to build a Spring Boot application
But I always got CORS problems like this
Access to XMLHttpRequest at '.../websocket-cr/info?t=1581585481804' from origin'http://localhost:8080'
has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin'
header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials.
Springboot service on 8082
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocketcr").
setAllowedOrigins("http://localhost:8080").withSockJS();
}
Angular service on 8080
var socket = new SockJS('http://localhost:8080/websocket-cr');
//socket.withCredentials = true ;
stompClient = Stomp.over(socket);
I have tried
setAllowedOrigins("http://localhost:8080").withSockJS();
setAllowedOrigins("*").withSockJS();
or use CORS Anywhere in javascript
var socket = new SockJS('http://cors-anywhere.herokuapp.com/http://localhost:8082/websocket-cr');
socket.withCredentials = true ;
and what is the best way to done that
should I make angular proxy to my backend server?
or it is ok by setAllowedOrigins('host:port')

In your Main class of the SpringBoot service , inject the below bean,it will work
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer () {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedMethods("GET", "POST", "PUT", "DELETE").allowedHeaders("*")
.allowedOrigins("*");
}
};
}

Sorry for coming late to this Party.
I am using here STOMP JS in angular 8 with springboot
working demo
you need to add WebSocketConfig class to configure things for Socket and For controller separately if you need it.
Configuration Class
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
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("/ws")
.setAllowedOrigins("*")
//.setAllowedOrigins("http://localhost:4200").setAllowedOrigins("http://localhost:8081")
.withSockJS();
}
}
ref Another Help from Spring people
In Controller Class just add
#Controller
#CrossOrigin(origins = "*")
public class LogsController {
..
}
And answer will be updated for authentication/authorization sooner and later.

Simply just add #CrossOrigin annotation on top of the class. It's work perfectly. For example:
#RestController
#CrossOrigin(origins = "*")
public class YourController {
.....
}

Related

spring boot websocket api 404 not found

In my spring boot app, I'm getting 404 error on the client side when I try to connect to the ws endpoint.
client side code
let wsUri = "ws://localhost:8080/chat"
let websocket = new WebSocket(wsUri);
spring config
package coffee.web;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(chatServer(), "/chat");
}
#Bean
public ChatServer chatServer() {
return new ChatServer();
}
}
Since the request is handled by dispatcher servlet as normal http request. so you need to add #Controller annotation to the WebSocketConfig class
#Configuration
#EnableWebSocket
#Controller
public class WebSocketConfig implements WebSocketConfigurer
i was getting 404 error then i have added the controller and then i was getting 403 as you are getting, 403 is like access restriction , so i have removed CSRF filter for that end point and then it is working. i hope it helps

Spring + restful + cors + not func

I am having a problem in my restful service with spring. Even after enabling CORS, I can not connect to my angular application.
#CrossOrigin
public class UsuarioController {
#Autowired
UsuarioService service;
#RequestMapping(method = RequestMethod.GET, value = "/lista_todos_usuarios", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Collection<Usuario>> buscaTodosUsuarios() {
Collection<Usuario> usuarios = service.buscaTodosUsuarios();
return new ResponseEntity<>(usuarios, HttpStatus.OK);
}
}
From Enabling Cross Origin Requests for a RESTful Web Service
In your case, I think you need indicate what origin is allowed to access the service.
In the example, the origin is http://localhost:9000. It should correspond to your Angular application.
Enabling CORS
Controller method CORS configuration
So that the RESTful web service will include CORS access control
headers in its response, you just have to add a #CrossOrigin
annotation to the handler method:
src/main/java/hello/GreetingController.java
#CrossOrigin(origins = "http://localhost:9000")
#GetMapping("/greeting")
public Greeting greeting(#RequestParam(required=false, defaultValue="World") String name) {
System.out.println("==== in greeting ====");
return new Greeting(counter.incrementAndGet(), String.format(template, name));
}
As a very simple workaround I could recommend to install CORS extension plugin for Chrome and use it during initial development stages.
If you want a global configuration, you may override method addCorsMappings of WebMvcConfigurerAdapter in your web configuration:
#Configuration
#EnableWebMvc
public class DispatcherContext extends WebMvcConfigurerAdapter {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedMethods("GET", "POST", "PUT", "PATCH", "DELETE", "HEAD");
}
}
Thanks Nikolay, sometimes we miss out on obvious things. Hehehe
in fact, I forgot to annotate the class with #RestController.

can i connect client server(localhost:8082) and websocket(localhost:8080)?

My problem is I need to connect different port or server!
websocket server(localhost:8080) client server(localhost:random)
(failed: Error during WebSocket handshake: Unexpected response code: 403)
why?? I'm tired...
I already tried same port and I can success!
I can connect client(localhost:8080) and websocekt(localhost:8080).
I want to use websocket using (server side: java sts, tomcat 8.0).
questions
1. websocket can connect only same server and port???!!!!(no! please!)
2. If I can... what's the problem :(? Do u have any example?
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(myHandler(), "/myHandler").setAllowedOrigins("*");
}
#Bean
public WebSocketHandler myHandler() {
return new MyHandler();
}
}
Sounds like this is related to CORS https://en.wikipedia.org/wiki/Cross-origin_resource_sharing.
I assume you are using spring websocket as you didn't mention.
You will need to disable same origin policy in your websocket config class as below example,
#Configuration
public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer {
#Override
protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) {
}
#Override
protected boolean sameOriginDisabled() {
//disable CSRF for websockets
return true;
}

How do I send a message in an Spring ApplicationListener (SessionConnectedEvent)

I'm using Stomp over SockJS with Spring messaging. I'm trying to send a message to all logged in users when a new user is connected. So first off here's my listener:
#Component
public class SessionConnectedListener implements ApplicationListener<SessionConnectedEvent> {
private static final Logger log = LoggerFactory.getLogger(SessionConnectedListener.class);
#Autowired
private SimpMessagingTemplate template;
#Override
public void onApplicationEvent(SessionConnectedEvent event) {
log.info(event.toString());
// Not sure if it's sending...?
template.convertAndSend("/topic/login", "New user logged in");
}
}
My WebSocket configurations
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("chat").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
}
}
My JS config
var socket = new SockJS('/chat');
stompClient = Stomp.over(socket);
stompClient.connect({}}, function(frame) {
// ... other working subscriptions
stompClient.subscribe("/topic/login", function(message) {
console.log(message.body);
});
});
My problem here is that my template.convertAndSend() doesn't work in the ApplicationListener. However, if I put it in a Controller method annotated with #MessageMapping, it will work and I will have a console log client side.
So my question is : Can template.convertAndSend() work in an ApplicationListener? If so, how? or am I missing something?
Thanks for the help!
PS : my log.info(event.toString()); works in the ApplicationListener so I know I'm getting into the onApplicationEvent() method.
Sending messages with the template within an ApplicationListener should work. Please check this Spring WebSocket Chat sample for an example.
Ok! So weird as it may be, I had my Listener in the following package:
package my.company.listener;
But because of a configuration I have in my App context, the convertAndSend() method wasn't working.
#ComponentScan(basePackages = { "my.company" }, excludeFilters = #ComponentScan.Filter(type = FilterType.REGEX, pattern = { "my.company.web.*" }))
However when I moved my Listener (Annotated with #Component) to the web sub-package, it worked!
package my.company.web.listener;

Spring WebSocket Connecting with SockJS to a different domain

WebSockets in Spring is a rather new topic that I;m tiring to find a bit more.
My problem is with connecting to a service from a different domain, I'm working on with Lineman building the front-end side and Spring Boot when doing the back-end side, with that I have these apps on two different ports : 8000 and 8080 on localhost.
I had issues with the 'Access-Control-Allow-Origin' header but I have resolved it by adding a filter on the server side which added the allowed origin to the header. After this I started to get the following error on connection:
GET http://localhost:8080/socket/info 403 (Forbidden)
AbstractXHRObject._start # sockjs-0.3.4.js:807
(anonymous function) #sockjs-0.3.4.js:841
I don't have Spring Security in the project so this is not an authorization issue, the error points to sockJS :
that.xhr.send(payload); - where payload is never defined.I tried but couldn't find the root of the call where is may began.
I was thinking if I need to add some additional information to either SockJS and Stomp when setting the connection, but there is not much of examples and notes in both wiki pages of this tools.
Bellow you will find the connection JS code.
var socket = new SockJS("http://localhost:8080/socket");
client = Stomp.over(socket);
client.connect({'login': BoatsGame.userName,
'passcode': 'guest'},
function (frame) {
....
The Server Side has a MessageBroker configured :
#Configuration
#EnableWebSocketMessageBroker
public class MessageBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Bean
public ServletServerContainerFactoryBean createWebSocketContainer() {
ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
container.setMaxTextMessageBufferSize(8192);
container.setMaxBinaryMessageBufferSize(8192);
return container;
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
//config.enableStompBrokerRelay("/queue", "/topic");
config.enableSimpleBroker("/queue", "/topic","/user");
config.setApplicationDestinationPrefixes("/BoatBattleGame");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/socket").withSockJS();
}
}
I Also tried setting up a MessageHandler as it has the option to set OriginAllowe when configuring, but I'm not sure how it is connected to the broker.
Last think, this setup works correctly when running on one port.
Jax's anwesr was correct :)
The registerStompEndpoints method gives us the opportunity to set the Allowed Origins.
We need to add it before the "withSockJs()" option.
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/BO/socket").setAllowedOrigins("*").withSockJS();
}
To anyone getting to this ticket because of the 403 Forbidden answer when trying to connect through a SockJsClient to a different domain:
The problem arises when trying to make a GET to the /info Url, as part of the handshaking. The response actually returns a 200 via WGET as well as via browser. Only through SockJsClient it doesn't work.
After trying different solutions, the only one that really fixed the issue is to write a class that implements Transport and InfoReceiver. In this way the developer can directly handle this part of the handshake.
Basically you make the work in the executeInfoRequest() method:
#Override
public String executeInfoRequest(URI infoUrl, HttpHeaders headers) {
HttpGet getRequest = new HttpGet(infoUrl); // eventually add headers here
HttpClient client = HttpClients.createDefault();
try {
HttpResponse response = client.execute(getRequest);
List<String> responseOutput = IOUtils.readLines(response.getEntity().getContent());
return responseOutput.get(0);
} catch (IOException ioe) {
...
}
}
I defined TransportType.XHR as transport type.
In my case, I had to add these configuarations to get SockJS / STOM to work with CORS:
#Configuration
#EnableWebMvc
public class WebConfig implements WebMvcConfigurer
{
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowCredentials(false)
.maxAge(3600)
.allowedHeaders("Accept", "Content-Type", "Origin",
"Authorization", "X-Auth-Token")
.exposedHeaders("X-Auth-Token", "Authorization")
.allowedMethods("POST", "GET", "DELETE", "PUT", "OPTIONS");
}
}
i found this solution by creating a Filter
package com.diool.notif.config;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
#Component
public class SimpleCORSFilter implements Filter {
private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(SimpleCORSFilter.class);
#Override
public void init(FilterConfig filterConfig) throws ServletException {
LOGGER.info("Initilisation du Middleware");
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest requestToUse = (HttpServletRequest)servletRequest;
HttpServletResponse responseToUse = (HttpServletResponse)servletResponse;
responseToUse.setHeader("Access-Control-Allow-Origin",requestToUse.getHeader("Origin"));
filterChain.doFilter(requestToUse,responseToUse);
}
#Override
public void destroy() {
}
}

Resources