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

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");
}
}

Related

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);
}
}

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

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);
}
}

Can't configure WebMvcConfigurer for interceptors addition in spring boot 2 application

I am trying to create an interceptor for the first time in my spring boot application, but somehow it is not created automatically, as described in the tutorials.
I've tried to create a WebConfig class that extends the WebMvcConfigurerAdapter class and annotated it as a #Component but it haven't worked. I also tried to create a WebConfig that implements the WebMvcConfigurer interface with #Configuration and #EnableWebMvc annotations but it hadn't worked either.
current WebConfig class:
#Configuration
#EnableWebMvc
#ComponentScan("com.*")
public class WebConfig implements WebMvcConfigurer {
public WebConfig() {
super();
}
#Autowired
HandlerInterceptor headerModifierInterceptor;
#Override
public void addInterceptors(InterceptorRegistry registry) {
System.out.println("------------------hi");
registry.addInterceptor(headerModifierInterceptor);
}
}
Application class
#SpringBootApplication
#EnableWebSecurity
#ComponentScan(basePackages = {"com.*"})
#EntityScan("com")
public class CoreRestAPIApplication {
public static void main(String[] args) {
SpringApplication.run(CoreRestAPIApplication.class, args);
}
}
My interceptor class:
#Component
public class RestTemplateHeaderModifierInterceptor
implements HandlerInterceptor {
#Autowired
AuthUtil authUtil;
#Autowired
JwtTokenProvider jwtTokenProvider;
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
String resolvedToken = jwtTokenProvider.resolveToken(request);
if (!StringUtils.isEmpty(resolvedToken)) {
String updatedToken = jwtTokenProvider.createToken(jwtTokenProvider.getUsername(resolvedToken), jwtTokenProvider.getAuthentication(resolvedToken).getAuthorities());
response.addHeader(authUtil.AUTH_HEADER_NAME, updatedToken);
}
}
}
After some search, I've found that I have a registered WebMvcConfigurationSupport configuration. However, if someone is looking and wishes to modify headers using an interceptor, DO NOT use an interceptor for that, as for spring will not handle it well if you return a ResponseEntity or your controller method returns a #ResponseBody.
Instead(at least for my use which is filtering and renewing a token every time a valid request is received) use the doFilterInternal method to add the header to the response(or add a cookie if you wish..) here is an example of how I did it:
public class JwtTokenFilter extends OncePerRequestFilter {
private JwtTokenProvider jwtTokenProvider;
public JwtTokenFilter(JwtTokenProvider jwtTokenProvider) {
this.jwtTokenProvider = jwtTokenProvider;
}
#Override
protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
String token = jwtTokenProvider.resolveToken(httpServletRequest);
try {
if (token != null && jwtTokenProvider.validateToken(token)) {
Authentication auth = jwtTokenProvider.getAuthentication(token);
SecurityContextHolder.getContext().setAuthentication(auth);
if(!jwtTokenProvider.isExpired(token)) {
httpServletResponse.setHeader("authKey", jwtTokenProvider.createToken(jwtTokenProvider.getUsername(token), auth.getAuthorities()));
}
}
} catch (ClientErrorException ex) {
//this is very important, since it guarantees the models is not authenticated at all
SecurityContextHolder.clearContext();
httpServletResponse.sendError(ex.getStatus().value(), ex.getMessage());
return;
}
filterChain.doFilter(httpServletRequest, httpServletResponse);
}
}

Spring Boot interceptor not called with custom handler mapping/adapter

In my Spring Boot 2 project I use a simple interceptor that was working fine. However after creating a custom HandlerAdapter and SimpleUrlHandlerMapping the interceptor never executed again.
public class RequestMonitoringInterceptor extends HandlerInterceptorAdapter {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
logger.debug("preHandle");
return super.preHandle(request, response, handler);
}
...
}
And registered in my WebConfig as:
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Bean
public RequestMonitoringInterceptor requestMonitoringInterceptor() {
return new RequestMonitoringInterceptor();
}
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(requestMonitoringInterceptor());
}
}
Any idea what I have missed?

PrincipalExtractor and AuthoritiesExtractor doesn't hit

I have a project with Spring security and Oauth2.
On the resource server I have the following configuration:
#Configuration
public class SecurityConfiguration extends ResourceServerConfigurerAdapter {
#Override
public void configure(final HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests().antMatchers("/info", "/health", "/h2-console/**").permitAll()
.anyRequest().authenticated()
.and().headers().frameOptions().disable();
}
}
I have the following extractors:
#Component
public class InsurancePrincipalExtractor implements PrincipalExtractor {
#Override
public Object extractPrincipal(Map<String, Object> map) {
return map.get("username");
}
}
#Component
public class InsuranceAuthoritiesExtractor implements AuthoritiesExtractor {
#Override
public List<GrantedAuthority> extractAuthorities(Map<String, Object> map) {
//Logic
}
I set the user-info-uri: http://localhost:8081/uaa/v1/me
The problem is that it does not hit my extractor methods at runtime, so nothing happens. As I know I just need to annotate it with the #Component and the Spring boot and will use it auto.
UPDATE:
Solution founded.
I had to add this to my configuration as well:
#Bean
protected ResourceServerTokenServices resourceServerTokenServices(ResourceServerProperties sso,
OAuth2ClientContext oauth2ClientContext,
UserInfoRestTemplateFactory restTemplateFactory) {
UserInfoTokenServices services = new UserInfoTokenServices(sso.getUserInfoUri(), sso.getClientId());
services.setRestTemplate(restTemplateFactory.getUserInfoRestTemplate());
services.setTokenType(sso.getTokenType());
return services;
}

Resources