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
Related
I've recently converted a GraphQL API from SpringBoot Web to WebFlux. In the previous version, the #RequestMapping was annotated with #CrossOrigin, which seemed to cover the OPTIONS HTTP verb.
In the new version, I am using the #MutationMapping / #QueryMapping annotations to map the schema to my methods, and responding to them reactively.
The problem is that for some of our frontend's, an OPTIONS preflight is sent and rejected with a 403 by this new implementation.
Is there an annotation or configuration I can enable where this preflight will not be rejected?
I've attempted to use the GraphQlWebFluxAutoConfiguration.GraphQlEndpointCorsConfiguration object however I can't seem to get it configured correctly. Any advice would be much appreciated, as I'm relatively new to the WebFlux stack.
Here's how I solved it - the issue was caused by my local dev environment having tomcat9, and the staging/qa environment I was deploying to having tomcat7 (not supporting WebFlux properly).
Please note that the app.enable-cors is a property defined in my application.properties file - you will need to add this if you are just copying+pasting this code.
I created two configuration classes, overriding both the Mvc and WebFlux Configurer.addCorsMapping(CorsRegistry registry) method. Depending on which auto configuration is loaded, the correct overriden method will be run. Code as follows:
First, the WebFlux version
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
#Configuration
#ConditionalOnProperty(name = "app.enable-cors", havingValue = "true")
public class GraphQLWebFluxConfiguration implements WebFluxConfigurer {
#Override
public void addCorsMappings(CorsRegistry corsRegistry){
corsRegistry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("*");
}
}
Secondly the MVC version
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
#ConditionalOnProperty(name = "app.enable-cors", havingValue = "true")
public class GraphQLWebMvcConfiguration implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry corsRegistry){
corsRegistry.addMapping("/**")
.allowedOrigins("*")
.allowedHeaders("*")
.allowedMethods("*");
}
}
I obviously have this disabled for the prod environment, but as a workaround for your QA/local environment it works well.
I have a SecurityConfig and I authorize requests with JWT tokens. The issue is that I would like to switch off the security config for testing my websockets, because somehow I always receiving 401...
I added:
#SpringBootApplication(exclude = SecurityConfig.class)
#EnableAutoConfiguration
public class MyApp {
but it is still authoraizing the requests from websockets and basically rest also...
anyone know how to switch of security in spring, or maybe how to allow websockets messages?
regards
You should add
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
#Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/**");
}
}
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 {
.....
}
I have a url mapping for /error to go to my errorPage.html, but it is not redirecting to this, and I get the normal default spring boot error screen.
package coffee.web;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("/index.html");
registry.addRedirectViewController("/admin", "admin/adminIndex.html");
registry.addViewController("/error").setViewName("/errorPage.html");
}
}
when I go to the url /error in the browser I get a status 999. when I go to an nonexistent address I get the default spring boot error page with a 404
ended up writing a explicit controller implement ErrorController
I have a simple service sending emails. It can be invoked using REST and JMS APIs. I want the requests to be validated before processing.
When I invoke it using REST I can see that org.springframework.validation.DataBinder invokes void validate(Object target, Errors errors, Object... validationHints) and then validator from Hibernate is invoked. This works as expected.
The problem is I can't achieve the same effect with JMS Listener. The listener is implemented as follows:
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import our.domain.mailing.Mailing;
import our.domain.mailing.jms.api.SendEmailFromTemplateRequest;
import our.domain.mailing.jms.api.SendSimpleEmailRequest;
import javax.validation.Valid;
#ConditionalOnProperty("jms.configuration.destination")
#Component
#AllArgsConstructor(onConstructor = #__(#Autowired))
#Slf4j
public class SendMailMessageListener {
Mailing mailing;
#JmsListener(destination = "${jms.configuration.destination}")
public void sendEmailUsingTemplate(#Valid SendEmailFromTemplateRequest request) {
log.debug("Received jms message: {}", request);
mailing.sendEmailTemplate(
request.getEmailDetails().getRecipients(),
request.getEmailDetails().getAccountType(),
request.getTemplateDetails().getTemplateCode(),
request.getTemplateDetails().getLanguage(),
request.getTemplateDetails().getParameters());
}
#JmsListener(destination = "${jms.configuration.destination}")
public void sendEmail(#Valid SendSimpleEmailRequest request) {
log.debug("Received jms message: {}", request);
mailing.sendEmail(
request.getRecipients(),
request.getSubject(),
request.getMessage());
}
}
The methods receive payloads but they are not validated. It's Spring Boot application and I have #EnableJms added. Can you guide what part of Spring source code is responsible for discovering #Validate and handling it? If you have any hints on how to make it running I would appreciate it a lot.
The solution is simple and was clearly described in official documentation: 29.6.3 Annotated endpoint method signature. There are few things you have to do:
Provide configuration implementing JmsListenerConfigurer (add #Configuration class implementing this interface)
Add annotation #EnableJms on the top of this configuration
Create bean DefaultMessageHandlerMethodFactory. It can be done in this configuration
Implement method void configureJmsListeners(JmsListenerEndpointRegistrar registrar) of interface JmsListenerConfigurer implemented by your configuration and set MessageHandlerMethodFactory using the bean you've just created
Add #Validated instead of #Valid to payload parameters
You can use #Valid in your listeners. Your answer was very close to it. In the step when you create DefaultMessageHandlerMethodFactory call .setValidator(validator) where validator is from org.springframework.validation. You can configure validator like this:
#Bean
public LocalValidatorFactoryBean configureValidator ()
{
return new LocalValidatorFactoryBean();
}
And then inject validator instance into your jms config