Spring Webflux Netty http and https - spring

I have a requirement where in a Spring Webflux Application I need to listen to HTTP and HTTPS port. I could easily configure HTTPS using
#Bean
public WebServerFactoryCustomizer<NettyReactiveWebServerFactory> customizer() {
return new WebServerFactoryCustomizer<NettyReactiveWebServerFactory>() {
#Override
void customize(NettyReactiveWebServerFactory factory) {
Ssl ssl = new Ssl()
// Your SSL Cusomizations
ssl.setEnabled(true)
ssl.setKeyStore(keystorePath)
ssl.setKeyPassword(keystorePass)
factory.setSsl(ssl)
}
}
}
I am not sure how to add HTTP listener for Netty. For Tomcat Servlet model I have found https://dzone.com/articles/enable-httphttps-spring-boot. Looking a similar setup for Netty Webflux.

This is not supported at the moment. You can watch the following issue in the meantime: https://github.com/spring-projects/spring-boot/issues/12035

Related

websocket spring boot setup

I have a spring boot application. I am trying to add the websocket piece to it. The problem is my angular client can't connect to it. I used smart websocket client google plugin, but still not able to connect. Here is the setup.
I am using Intellij Idea on localhost. the spring boot application is running on localhost:8080. I can see the WebSocketSession is runnign from intellij idea console.
Here is the setup:
#Slf4j
#RestController
public class WebsocketController {
#MessageMapping("/ws-on/hello")
#SendTo("/ws-on/greetings")
public UserStateIndicator greeting(UserStateIndicator indicator) throws Exception {
Thread.sleep(1000); // simulated delay
log.debug("websocket " + indicator.toString());
return indicator;
}
}
#Configuration
#EnableWebSocketMessageBroker
public class WebsocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/ws-on");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws-on")
.setAllowedOrigins("http://localhost:4200")
.withSockJS();
}
}
my angular is running on localhost:4200.
I used ws://localhost:8080/ws-on as the url from StompJS to connect.
My question is how do I find the websocket url to connect, and how do I know the websocket is running on the spring boot server?
finally I figured it out. Because I am using SockJS on both angular and spring boot, so, the URL is actaully http not ws. the correct url to connect is then http://localost:8080/ws-on

How to intercept actuator health request when actuator health is running on a different port

I am working on a Spring Boot project where actuator health is configured on port 8090 where as application on 8080.
I would like to intercept incoming actuator health requests in order to customize it.
Tried adding an interceptor as below:
public class ActuatorConfig extends WebMvcEndpointManagementContextConfiguration {
#Override
public WebMvcEndpointHandlerMapping webMvcEndpointHandlerMapping(.....) {
WebMvcEndpointHandlerMapping mapping = super.webMvcEndpointHandlerMapping(...);
mapping.setInterceptors(myActuatorInterceptor());
}
#Bean
public MyActuatorInterceptor myActuatorInterceptor(){
return new MyActuatorInterceptor();
}
}
public class MyActuatorInterceptor extends HandlerInterceptor {
#Override
public boolean preHandle(...) {
//my customization code
}
}
When I add the above Interceptor strangely '/actuator/health' requests return '404 not found' on port 8090 even though it exists where as it's working fine on port 8080.
When I disable Interceptor code '/actuator/health' requests works fine on 8090.
Question: How to intercept '/actuator/health' request when its running on a different port?

How to configure in Spring Boot 2 (w/ WebFlux) two ports for HTTP and HTTPS?

Can anybody tell me how 2 ports (for HTTP and HTTPS) can be configured when using Spring Boot 2 and WebFlux? Any hint is appreciated!
This isn't directly supported by Spring Boot 2 yet.
But, you may be able to get it to work in a couple of ways.
By default, Spring Boot WebFlux uses Netty. If you are already configured for ssl, then Spring Boot will start up and open port 8443 (or whatever you have configured).
Then, to add 8080, you can do:
#Autowired
HttpHandler httpHandler;
WebServer http;
#PostConstruct
public void start() {
ReactiveWebServerFactory factory = new NettyReactiveWebServerFactory(8080);
this.http = factory.getWebServer(this.httpHandler);
this.http.start();
}
#PreDestroy
public void stop() {
this.http.stop();
}
Which is a bit clunky since your https configuration is in one spot (application.yml) and your http configuration is in Java config, but I have tested this myself with a toy application. Not sure how robust of a solution it is, though.
Another option that may work is to try the equivalent of other suggestions, but use the reactive version of the class, which is TomcatReactiveWebServerFactory. I'm not aware of any way to provide more than one connector for it, but you could possibly override its getWebServer method:
#Bean
TomcatReactiveWebServerFactory twoPorts() {
return new TomcatReactiveWebServerFactory(8443) {
#Override
public WebServer getWebServer(HttpHandler httpHandler) {
// .. copy lines from parent class
// .. and add your own Connector, similar to how tutorials describe for servlet-based support
}
}
}
Also, a bit messy, and I have not tried that approach myself.
Of course, keep track of the ticket so you know when Spring Boot 2 provides official support.
Follow the instructions listed in the link provided by jojo_berlin (here's the link). Instead of using his EmbeddedTomcatConfiguration class though, use this below
#Configuration
public class TomcatConfig {
#Value("${server.http.port}")
private int httpPort;
#Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setPort(httpPort);
factory.addAdditionalTomcatConnectors(connector);
return factory;
}
}
Actually you can define a second connector as described here . So you can define a https connector as your default and an additional HTTP Connector

How to expose both a SOAP web-service and RESTful API at the same time in Spring Boot?

In Spring Boot 1.4.3 I exposed a SOAP web service endpoint which works successfully on port 8080.
For the purpose of running a health check I also need to expose a RESTful API. I tried both using Actuator and a rest controller:
#RestController
public class RESTapis {
#RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, value = "/health")
public String healthCheck() {
return "ACK";
}
}
but in both cases I get the same response: HTTP 405 (method not allowed).
The REST api returns HTTP 200 if I disable the web-service.
How can I have both the SOAP web-service and REST working at the same time?
Here is the web-service configuration:
#EnableWs
#Configuration
public class WebServiceConfig extends WsConfigurerAdapter {
#Bean
public ServletRegistrationBean messageDispatcherServlet(ApplicationContext applicationContext) {
MessageDispatcherServlet servlet = new MessageDispatcherServlet();
servlet.setApplicationContext(applicationContext);
servlet.setTransformWsdlLocations(true);
return new ServletRegistrationBean(servlet, "/*");
}
}
So going off the messageDispatcherServlet method it looks like you are binding your servlet to all the incoming request due to the wildcard registration:
return new ServletRegistrationBean(servlet, "/*");
Hence the MessageDispatcher is intercepting all of your incoming requests and trying to find the /health and throwing http 405.
Fix:
return new ServletRegistrationBean(servlet, "/soap-api/*");
Explanation:
By binding the Message Dispatcher to a specific URI namespace we can ensure that all the request fired on the /soap-api/* namespace ONLY will be intercepted by the MessageDispatcher. And all the other requests will be intercepted by the DispatcherServlet making it possible to run a Rest interface in parallel to your Soap WS.
Suggestion:
Not knowing the reasons / specifics of the app, but going off the name of the method healthcheck(), you can look at using spring boot actuator to generate health checks, metrics for you app. You can also override it for customisations.
Reference for actuator: https://spring.io/guides/gs/actuator-service/

How to redirect automatically to https with Spring Boot

How I can easily configure the embedded tomcat server to redirect all http traffic to https? I have Spring Boot running on an ec2 instance that is behind an elastic load balancer. I have configured the ELB to handle ssl for me (which is awesome) and it sets the X-FORWARDED-PROTO header to "https". I want to detect when that isn't set, and redirect the user to force them to use https if they aren't already.
So far, I have tried adding the following to my application.properties file with no luck:
server.tomcat.protocol-header=x-forwarded-proto
security.require-ssl=true
My answer is a little late but I just recently had this problem and want to post a solution which worked for me.
Originally, I thought that setting tomcat up to use the X-Forwarded headers would suffice but the RemoteIPValve from Tomcat, which should normally handle this case, didnt work for me.
My solution was to add an EmbeddedServletContainerCustomizer and add a ConnectorCustomizer:
(note that I am using Tomcat 8 here)
#Component
public class TomcatContainerCustomizer implements EmbeddedServletContainerCustomizer {
private static final Logger LOGGER = LoggerFactory.getLogger(TomcatContainerCustomizer.class);
#Override
public void customize(final ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
final TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container;
tomcat.addConnectorCustomizers(connector -> {
connector.setScheme("https");
connector.setProxyPort(443);
});
LOGGER.info("Enabled secure scheme (https).");
} else {
LOGGER.warn("Could not change protocol scheme because Tomcat is not used as servlet container.");
}
}
}
The important thing is that you not only set the Scheme to https but also the ProxyPort without which all internal redirects from Spring Boot were routed to port 80.
The configuration property security.require-ssl doesn't work when basic authentication is disabled (at least on old versions of Spring Boot). So you probably need to secure all the requests manually with code similar to this one:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Inject private SecurityProperties securityProperties;
#Override
protected void configure(HttpSecurity http) throws Exception {
if (securityProperties.isRequireSsl()) http.requiresChannel().anyRequest().requiresSecure();
}
}
You can check my full answer here: Spring Boot redirect HTTP to HTTPS
You will need a keystore file and few config classes.
The below link explains it in detail.
Https on embedded tomcat
Spring Boot 2.0 redirection of http to https:
Add the following to the #Configuration
#Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector(
TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}

Resources