How to troubleshoot and resolve 404 on SockJs's /info path when with cross-origin - websocket

I'm having trouble with SockJs and CORS. I use spring. I set up the WebMvcConfigured as follows:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry
.addMapping("/**")
.allowedOrigins("http://localhost:3000")
.allowCredentials(true);
;
}
};
}
and WebSocketConfig as follows:
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/gs-guide-websocket")
.setAllowedOrigins("*")
.withSockJS();
}
However, when my web client tries to GET against /gs-guide-websocket/info , it gets a 404.
SockJs's specification requires a /info path to be present. It seems like even though I did use .withSockJS() on the server side, it did not set up the /gs-guide-websocket/info path.
How should I create this path through spring and/or spring-websocket?
Thank you,
Kevin

I am not using the STOMP protocol, whereas I have configured the websocket with SockJS which is working fine for me.
Here the message payload has sent as response to the frontend.
CustomWebSocketHandler.java
#Component
public class CustomWebSocketHandler extends TextWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payload = message.getPayload();
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("message", "Session started");
jsonObject.addProperty("payload", payload);
session.sendMessage(new TextMessage(new Gson().toJson(jsonObject)));
}
}
WebSocketConfig.java
#EnableWebSocket
#Configuration
public class WebSocketConfig implements WebSocketConfigurer {
#Autowired
private CustomWebSocketHandler customWebSocketHandler;
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry
.addHandler(customWebSocketHandler, "/socket")
.setAllowedOrigins("*")
.withSockJS()
.setHeartbeatTime(20);
}
}

Related

Identifying multiple STOMP endpoints in message controller

Is it possible to route different STOMP endpoints for each #MessageMapping?
For example, if I set my WebSocketMessageBrokerConfigurer like this:
#EnableWebSocketMessageBroker
#Configuration
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ep1", "/ep2").setAllowedOriginPatterns("*").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
Then, how can I specify /ep1 & /ep2 for each #MessageMapping in my controller?

Intercept request & send to external

I am developing a Spring boot project.
One example of my controller:
#Controller
public class RestController {
#GetMapping(value = "/student/{studentId}")
public #ResponseBody Student getData(#PathVariable Integer studentId) {
Student student = new Student();
student.setName("Peter");
student.setId(studentId);
return student;
}
}
I have other endpoints implemented as well.
I need to intercept every request hits my endpoints & forward the request to another service (microservice), in other words, I need to forward each request to another web app running on the same local machine as current one, based on the response from that service to decide whether proceed forward the request or not.
My rough idea is to use HandlerIntercept , but I am not sure whether I am going to the right direction. Could someone please share some experiences what is the best way to achieve this? It would be nice if you could show some sample code. Thanks in advance.
You can use HandlerInterceptorAdapter.
Define the Interceptor as below.
#Component
public class RequestInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object object) throws Exception {
System.out.println("In preHandle we are Intercepting the Request");
System.out.println("____________________________________________");
//Call the Rest API and Validate
if (conditionsNotMet()) {
response.getWriter().write("something");
response.setStatus(someErrorCode);
return false;
}
}
}
Register the HandlerInterceptorAdapter
#Configuration
public class PathMatchingConfigurationAdapter extends WebMvcConfigurerAdapter {
#Autowired
private RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor);
}
}
using WebMvcConfigurer
#Configuration
public class PathMatchingConfigurationAdapter implements WebMvcConfigurer {
#Autowired
private RequestInterceptor requestInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestInterceptor);
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry resourceHandlerRegistry) {
}
#Override
public void addCorsMappings(CorsRegistry corsRegistry) {
}
#Override
public void addViewControllers(ViewControllerRegistry viewControllerRegistry) {
}
#Override
public void configureViewResolvers(ViewResolverRegistry viewResolverRegistry) {
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> list) {
}
#Override
public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> list) {
}
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> list) {
}
#Override
public void extendMessageConverters(List<HttpMessageConverter<?>> list) {
}
#Override
public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
#Override
public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> list) {
}
#Override
public Validator getValidator() {
return null;
}
#Override
public MessageCodesResolver getMessageCodesResolver() {
return null;
}
#Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(false);
}
#Override
public void configureAsyncSupport(AsyncSupportConfigurer asyncSupportConfigurer) {
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer defaultServletHandlerConfigurer) {
}
#Override
public void addFormatters(FormatterRegistry formatterRegistry) {
}
#Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
}

What is the ServletFilter equalent of a RabbitMq Listener?

I have a spring-boot web application for which I implemented an MDCFilter that adds a UUID to MDC logging context that i can find in the log file.
The Filter class looks like this.
public class MDCFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) {
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
String requestId = UUID.randomUUID().toString();
MDC.put(REQUEST_ID_KEY, requestId);
response.addHeader("trace", requestId);
try {
chain.doFilter(req, resp);
} finally {
MDC.remove("trace");
}
}
#Override
public void destroy() {
}
}
But recently we moved towards processing traffic via Queues and I have no clue from the documents to replicate this filter behaviour for the message listeners.
My listener would look something like this.
#RabbitListener(queues = "${queue1}")
public void receiveMessages(Message message) {
doTheBusinessLogic(message)
}
Can anyone point me to the right direction ?
Use the container's adviceChain. Assuming you are using Boot 2.0 and the simple container factory, override boot's factory to add the advice...
#SpringBootApplication
public class So49770881Application {
public static void main(String[] args) {
SpringApplication.run(So49770881Application.class, args);
}
#Bean(name = "rabbitListenerContainerFactory")
public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(
SimpleRabbitListenerContainerFactoryConfigurer configurer,
ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setAdviceChain(new MDCAdvice());
return factory;
}
public static class MDCAdvice implements MethodInterceptor {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
// pre process
try {
return invocation.proceed();
}
finally {
// post process
}
}
}
}

WebSocket, Spring Security integration

I'm having small REST API application that is running on Spring boot. For security I'm using external provider (Auth0 in this case), and frontend Angular application provide token for each API call. This works great with minimal configuration:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
JwtWebSecurityConfigurer
.forRS256(apiAudience, issuer)
.configure(http)
.authorizeRequests()
.antMatchers("/websocket/**").permitAll()
.anyRequest().authenticated();
}
}
Now I'm trying to add some websocket support in it for notify users on some events. Some basic things:
#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("/websocket").withSockJS();
}
}
Connection is working and I can introduce some HandshakeInterceptor to validate user's token that is sent throw url on connect:
public class HttpSessionHandshakeInterceptor implements HandshakeInterceptor {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
String token = httpServletRequest.getParameter("token");
AuthAPI auth = new AuthAPI("account url", "user id", "secret");
Request<UserInfo> authRequest = auth.userInfo(token);
try {
UserInfo info = authRequest.execute();
if (info.getValues().get("name") != null) {
return true;
}
} catch (APIException exception) {
} catch (Auth0Exception exception) {
}
response.setStatusCode(HttpStatus.UNAUTHORIZED);
return false;
}
}
I'm having problem with current API calls that I want to be available also on sockets:
#Controller
public class TestController {
#PreAuthorize("hasAuthority('read:photos')")
#RequestMapping(value = "/photos", method = RequestMethod.GET)
#MessageMapping("/photos")
#ResponseBody
public String getPhotos() {
return "All good. You can see this because you are Authenticated with a Token granted the 'read:photos' scope";
}
}
Calling this from socket throws that, An Authentication object was not found in the SecurityContext exception. Is their any why to provide SecurityContext on socket calls? Maybe throw ChannelInterceptorAdapter.preSend? I found a lot of questions about this, but no answers have given (example). Auth0 team also not provide any working example of this.
I also tried to use WebSocket Security, but cant rewire it to Auth0.
Do anyone have any working solution with this more granular approach? Small note, on frontend using SockJS and Stomp. Can send token throw headers or throw url.

Spring Websocket Configuration: using WebSocketMessageBrokerConfigurationSupport and WebSocketConfigurer together - how?

I am configuring currently my Spring Websocket using the class
public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
now I came across the advice Spring STOMP Websockets: any way to enable permessage-deflate on server side?
that makes use of
public class SampleJettyWebSocketsApplication implements WebSocketConfigurer
and overrides
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry)
and offers
#Bean
public DefaultHandshakeHandler handshakeHandler()
Question, what is the relation between WebSocketConfigurer and WebSocketMessageBrokerConfigurationSupport? In other words, can I possibly somehow add configuration from WebSocketConfigurer implementation via API of the first class, WebSocketMessageBrokerConfigurationSupport, so all configuration remains in one single file?
The WebSocketMessageBrokerConfigurationSupport implementation is DelegatingWebSocketMessageBrokerConfiguration which is configured via #EnableWebSocketMessageBroker. All you need in your custom code is WebSocketMessageBrokerConfigurer implementation. And that one is injected into DelegatingWebSocketMessageBrokerConfiguration:
#Autowired(required = false)
public void setConfigurers(List<WebSocketMessageBrokerConfigurer> configurers) {
This is a sample config from my test-cases:
#Configuration
#EnableWebSocketMessageBroker
static class ServerConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Bean
public DefaultHandshakeHandler handshakeHandler() {
return new DefaultHandshakeHandler(new TomcatRequestUpgradeStrategy());
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws")
.setHandshakeHandler(handshakeHandler())
.setAllowedOrigins("http://foo.com")
.addInterceptors(new HandshakeInterceptor() {
#Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
return request.getHeaders().getOrigin() != null;
}
#Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception exception) {
}
})
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry configurer) {
configurer.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/topic", "/queue");
}
}

Resources