How To By-pass SSL Certificate Validation in spring-boot-admin? - spring-boot

So I'm able to disable the SBA Admin server from validating SSL certs from the clients it tries to connect to - for SBA version 2.6.2 it is more or less outlined right from their documentation: https://codecentric.github.io/spring-boot-admin/current/#_using_mutual_tls
Here is the complete configuration overriding bean:
package com.markham.mkmappadmin.config;
import javax.net.ssl.SSLException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import reactor.netty.http.client.HttpClient;
/**
* Custom http client class which overrides Spring Boot Admin's server default client.<br>
* The custom client will bypass any SSL Validation by configuring an instance of
* {#link InsecureTrustManagerFactory}
* #author Hanif Rajabali
* #see Spring Boot Admin 2.6.2 Using Mutual TLS
*/
#Configuration
public class CustomHttpClientConfig {
#Bean
public ClientHttpConnector customHttpClient() throws SSLException {
SslContext sslContext = SslContextBuilder
.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient httpClient = HttpClient.create().secure(
ssl -> ssl.sslContext(sslContext)
);
return new ReactorClientHttpConnector(httpClient);
}
}
What I still haven't figured out is how to disable it from the SBA client. I have a custom RestTemplate Config defined below, but the SBA client doesn't seem to be picking it up even though I see that the SBA client code is using the BlockingRegistrationClient i.e) RestTemplate
package com.markham.mkmemailerws.config;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* Need to explicitly build Spring Boot's auto configured
* {#link #restTemplate(RestTemplateBuilder)}
*
* #author Hanif Rajabali
*
*/
#Configuration
public class RestTemplateConfig {
// #Bean
// public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
// return restTemplateBuilder.build();
// }
/**
* The following will bypass ssl validation altogether. Not ideal.
*/
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder)
throws NoSuchAlgorithmException, KeyManagementException {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
} };
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build();
HttpComponentsClientHttpRequestFactory customRequestFactory = new HttpComponentsClientHttpRequestFactory();
customRequestFactory.setHttpClient(httpClient);
return builder.requestFactory(() -> customRequestFactory).build();
}
}

Related

send data from spring boot to client with stomp websocket

I want to send data from Boot(Server) to client!
I am using stomp websocket!
WebSocketClient doesn't work with error that the HTTP response from the server [200] did not permit the HTTP upgrade to WebSocket!
It's my Java Stomp WebSocket client code!
In this code, I tried to config handShakeHeader.... but it doesn't work!
package refill.station.websocket;
import org.junit.jupiter.api.Test;
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandler;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.scheduling.concurrent.ConcurrentTaskScheduler;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.WebSocketClient;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
public class WebSocketTest {
#Test
public void WebSocketTest() {
WebSocketClient webSocketClient = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(webSocketClient);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
stompClient.setTaskScheduler(new ConcurrentTaskScheduler());
String url = "ws://localhost:8080/ws/websocket";
StompSessionHandler sessionHandler = new StompSessionHandlerAdapter() {};
List<String> protocols = new ArrayList<>();
protocols.add("v10.stomp");
protocols.add("v11.stomp");
WebSocketHttpHeaders handSahkeHeaders = new WebSocketHttpHeaders();
handSahkeHeaders.setUpgrade("websocket");
handSahkeHeaders.setConnection("Upgrade");
handSahkeHeaders.setSecWebSocketVersion("13");
handSahkeHeaders.setSecWebSocketKey("4hb9p5/JjeKLed5aXlBqnw==");
handSahkeHeaders.setSecWebSocketProtocol(protocols);
StompHeaders stompHeaders = new StompHeaders();
StompSession stompSession = null;
try {
stompSession = stompClient.connect(URI.create(url), handSahkeHeaders, stompHeaders, sessionHandler).get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
stompSession.send("/app/control/complete", "msg");
// StompSession stompSession = stompClient.connect(url, sessionHandler).get();
}
}
I checked my WebSocket server with javascript and it works correctly!
This is my WebSocket server configuration!
package refill.station.config.websocket;
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;
import org.springframework.web.socket.server.RequestUpgradeStrategy;
import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
RequestUpgradeStrategy upgradeStrategy = new TomcatRequestUpgradeStrategy();
registry.addEndpoint("/ws")
.withSockJS();
registry.addEndpoint("/ws")
.setHandshakeHandler(new DefaultHandshakeHandler(upgradeStrategy))
.setAllowedOrigins("*");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/control");
}
}
last, it's my controller for websocket!
package refill.station.controller.websocket;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
#RequiredArgsConstructor
#RestController
#Slf4j
public class RefillerCommunicateController {
#MessageMapping("/control/complete")
#SendTo("/control/operate")
public String test(#Payload String data) {
log.info("RefillerCommunicateController-test method");
return "test";
}
}

spring batch integration configuration with azure service bus

I am trying to configure inbound and outbound adaptors as provided in the spring batch remote partitioning samples for Manager and worker beans. Having difficulty since they are configured in context of AMQPConnectionFactory.
However when I follow spring integration samples, there is no class which can provide Connection Factory. Help appreciated.
Below is sample code:-
import com.microsoft.azure.spring.integration.core.DefaultMessageHandler;
import com.microsoft.azure.spring.integration.core.api.CheckpointConfig;
import com.microsoft.azure.spring.integration.core.api.CheckpointMode;
import com.microsoft.azure.spring.integration.servicebus.inbound.ServiceBusQueueInboundChannelAdapter;
import com.microsoft.azure.spring.integration.servicebus.queue.ServiceBusQueueOperation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.annotation.JobBuilderFactory;
import org.springframework.batch.integration.partition.RemotePartitioningManagerStepBuilderFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.dsl.IntegrationFlow;
import org.springframework.integration.dsl.IntegrationFlows;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.util.concurrent.ListenableFutureCallback;
#Configuration
#IntegrationComponentScan
public class ManagerConfiguration {
private static final int GRID_SIZE = 3;
private static final String REQUEST_QUEUE_NAME = "digital.intg.batch.cm.request";
private static final String REPLY_QUEUE_NAME = "digital.intg.batch.cm.reply";
private static final String MANAGER_INPUT_CHANNEL = "manager.input";
private static final String MANGER_OUTPUT_CHANNEL = "manager.output";
private static final Log LOGGER = LogFactory.getLog(ManagerConfiguration.class);
private final JobBuilderFactory jobBuilderFactory;
private final RemotePartitioningManagerStepBuilderFactory managerStepBuilderFactory;
public ManagerConfiguration(JobBuilderFactory jobBuilderFactory,
RemotePartitioningManagerStepBuilderFactory managerStepBuilderFactory
) {
this.jobBuilderFactory = jobBuilderFactory;
this.managerStepBuilderFactory = managerStepBuilderFactory;
}
/*
* Configure outbound flow (requests going to workers)
*/
#Bean( name = MANGER_OUTPUT_CHANNEL )
public DirectChannel managerRequests() {
return new DirectChannel();
}
/*
* Configure inbound flow (replies coming from workers)
*/
#Bean( name = MANAGER_INPUT_CHANNEL )
public DirectChannel managerReplies() {
return new DirectChannel();
}
#Bean
public ServiceBusQueueInboundChannelAdapter managerQueueMessageChannelAdapter(
#Qualifier( MANAGER_INPUT_CHANNEL ) MessageChannel inputChannel, ServiceBusQueueOperation queueOperation) {
queueOperation.setCheckpointConfig(CheckpointConfig.builder().checkpointMode(CheckpointMode.MANUAL).build());
ServiceBusQueueInboundChannelAdapter adapter = new ServiceBusQueueInboundChannelAdapter(REPLY_QUEUE_NAME,
queueOperation);
adapter.setOutputChannel(inputChannel);
return adapter;
}
#Bean
#ServiceActivator( inputChannel = MANGER_OUTPUT_CHANNEL )
public MessageHandler managerQueueMessageSender(ServiceBusQueueOperation queueOperation) {
DefaultMessageHandler handler = new DefaultMessageHandler(REQUEST_QUEUE_NAME, queueOperation);
handler.setSendCallback(new ListenableFutureCallback<Void>() {
#Override
public void onSuccess(Void result) {
LOGGER.info("Manager Request Message was sent successfully.");
}
#Override
public void onFailure(Throwable ex) {
LOGGER.info("There was an error sending request message to worker.");
}
});
return handler;
}
#Bean
public IntegrationFlow managerOutboundFlow(MessageHandler managerQueueMessageSender) {
return IntegrationFlows
.from(managerRequests())
.handle(managerQueueMessageSender)
.get();
}
#Bean
public IntegrationFlow managerInboundFlow(ServiceBusQueueInboundChannelAdapter managerQueueMessageChannelAdapter) {
return IntegrationFlows
.from(managerQueueMessageChannelAdapter)
.channel(managerReplies())
.get();
}
/*
* Configure the manager step
*/
#Bean
public Step managerStep() {
return this.managerStepBuilderFactory.get("managerStep")
.partitioner("workerStep", new BasicPartitioner())
.gridSize(GRID_SIZE)
.outputChannel(managerRequests())
.inputChannel(managerReplies())
//.aggregator()
.build();
}
#Bean
public Job remotePartitioningJob() {
return this.jobBuilderFactory.get("remotePartitioningJob")
.start(managerStep())
.build();
}
}
The sample uses ActiveMQ because it is easily embeddable in a JVM for our tests and samples. But you can use any other broker that you want.
?? what should I inject here?
You should inject any dependency required by the queueMessageChannelAdapter handler:
.handle(queueMessageChannelAdapter)

How avoid the certificate validation in spring-boot-admin?

In what way can I avoid certificate validation in spring-boot-admin?
Link error image:
https://ibb.co/fkZu8y
I configure the RestTemplate for avoid the certificate in a class, but I do not know how to send it, I guess it must be in the client, the spring-boot-admin-starter-client works automatically.
This is the code for avoid the certificate validation.
public class SSLUtil {
public RestTemplate getRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
return true;
}
};
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier());
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(csf).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
}
Application.properties
spring.application.name=Admin-Application
server.port=1111
security.user.name=admin
security.user.password=admin123
#Configuration
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// Page with login form is served as /login.html and does a POST on
// /login
http.formLogin().loginPage("/login.html").loginProcessingUrl("/login").permitAll();
// The UI does a POST on /logout on logout
http.logout().logoutUrl("/logout");
// The ui currently doesn't support csrf
http.csrf().disable().authorizeRequests()
// Requests for the login page and the static assets are
// allowed
// http.authorizeRequests()
.antMatchers("/login.html", "/**/*.css", "/img/**", "/third-party/**").permitAll();
// ... and any other request needs to be authorized
http.authorizeRequests().antMatchers("/**").authenticated();
// Enable so that the clients can authenticate via HTTP basic for
// registering
http.httpBasic();
}
}
I'm using Spring Boot Admin 2.1.3 together with Eureka.
It seems SBA has moved from RestTemplate to WebClient. So I create a WebClient which has a SSLContext with a trust manager set to InsecureTrustManagerFactory, that trusts everything. Then I use this webclient and instantiate SBA's InstanceWebClient. Not sure if there is an easier approach, but this worked for me.
import de.codecentric.boot.admin.server.config.AdminServerProperties;
import de.codecentric.boot.admin.server.web.client.HttpHeadersProvider;
import de.codecentric.boot.admin.server.web.client.InstanceExchangeFilterFunction;
import de.codecentric.boot.admin.server.web.client.InstanceWebClient;
import io.netty.channel.ChannelOption;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.handler.timeout.ReadTimeoutHandler;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.ConnectionObserver;
import reactor.netty.http.client.HttpClient;
import javax.net.ssl.SSLException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
#Configuration
#EnableConfigurationProperties(AdminServerProperties.class)
public class SslConfiguration {
private final AdminServerProperties adminServerProperties;
public SslConfiguration(AdminServerProperties adminServerProperties) {
this.adminServerProperties = adminServerProperties;
}
#Bean
public InstanceWebClient instanceWebClient(HttpHeadersProvider httpHeadersProvider,
ObjectProvider<List<InstanceExchangeFilterFunction>> filtersProvider) throws SSLException {
List<InstanceExchangeFilterFunction> additionalFilters = filtersProvider.getIfAvailable(Collections::emptyList);
return InstanceWebClient.builder()
.defaultRetries(adminServerProperties.getMonitor().getDefaultRetries())
.retries(adminServerProperties.getMonitor().getRetries())
.httpHeadersProvider(httpHeadersProvider)
.webClient(getWebClient())
.filters(filters -> filters.addAll(additionalFilters))
.build();
}
private WebClient getWebClient() throws SSLException {
SslContext sslContext = SslContextBuilder
.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient httpClient = HttpClient.create()
.compress(true)
.secure(t -> t.sslContext(sslContext))
.tcpConfiguration(tcp -> tcp.bootstrap(bootstrap -> bootstrap.option(
ChannelOption.CONNECT_TIMEOUT_MILLIS,
(int) adminServerProperties.getMonitor().getConnectTimeout().toMillis()
)).observe((connection, newState) -> {
if (ConnectionObserver.State.CONNECTED.equals(newState)) {
connection.addHandlerLast(new ReadTimeoutHandler(adminServerProperties.getMonitor().getReadTimeout().toMillis(),
TimeUnit.MILLISECONDS
));
}
}));
ReactorClientHttpConnector reactorClientHttpConnector = new ReactorClientHttpConnector(httpClient);
return WebClient.builder().clientConnector(reactorClientHttpConnector).build();
}
}
To disable the SBA Admin server from validating SSL certs from the clients it tries to connect to, you can use the following:
For SBA version 2.6.2 it is more or less outlined right from their documentation: https://codecentric.github.io/spring-boot-admin/current/#_using_mutual_tls
Here is the complete configuration overriding bean:
package com.markham.mkmappadmin.config;
import javax.net.ssl.SSLException;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import reactor.netty.http.client.HttpClient;
/**
* Custom http client class which overrides Spring Boot Admin's server default client.<br>
* The custom client will bypass any SSL Validation by configuring an instance of
* {#link InsecureTrustManagerFactory}
* #author Hanif Rajabali
* #see Spring Boot Admin 2.6.2 Using Mutual TLS
*/
#Configuration
public class CustomHttpClientConfig {
#Bean
public ClientHttpConnector customHttpClient() throws SSLException {
SslContext sslContext = SslContextBuilder
.forClient()
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
HttpClient httpClient = HttpClient.create().secure(
ssl -> ssl.sslContext(sslContext)
);
return new ReactorClientHttpConnector(httpClient);
}
}
What I still haven't figured out is how to disable it from the SBA client. I have a custom RestTemplate Config defined below, but the SBA client doesn't seem to be picking it up even though I see that the SBA client code is using the BlockingRegistrationClient i.e) RestTemplate
package com.markham.mkmemailerws.config;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
* Need to explicitly build Spring Boot's auto configured
* {#link #restTemplate(RestTemplateBuilder)}
*
* #author Hanif Rajabali
*
*/
#Configuration
public class RestTemplateConfig {
// #Bean
// public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
// return restTemplateBuilder.build();
// }
/**
* The following will bypass ssl validation altogether. Not ideal.
*/
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder)
throws NoSuchAlgorithmException, KeyManagementException {
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
public void checkClientTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(java.security.cert.X509Certificate[] certs, String authType) {
}
} };
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
CloseableHttpClient httpClient = HttpClients.custom().setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE).build();
HttpComponentsClientHttpRequestFactory customRequestFactory = new HttpComponentsClientHttpRequestFactory();
customRequestFactory.setHttpClient(httpClient);
return builder.requestFactory(() -> customRequestFactory).build();
}
}
Try http.csrf().disable().authorizeRequests()
Above code will disable csrf token. Below is my code for OAuth where I disabled csrf to reduce complexity.
#RestController
#EnableOAuth2Sso
#EnableResourceServer
#SpringBootApplication
public class SpringBootWebApplication extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().authorizeRequests()
.antMatchers("/api/**", "/dashboard", "/welcome","/about").authenticated().antMatchers("/**").permitAll()
.anyRequest().authenticated().and().logout().logoutSuccessUrl("/").permitAll();
}

Where is 'UndertowEmbeddedServletContainerFactory' in Spring Boot 2.0.0?

I'm trying to upgrade an existing project to Spring 5 and Spring Boot 2.0.0. There is a class that extends UndertowEmbeddedServletContainerFactory, which is supposed to be in the org.springframework.boot.context.embedded.undertow package. However, I can't see this package or interface in the current Spring Boot 2.0.0.M1/M2/M3/BUILD-SNAPSHOT. The documentation still seems to reference this interface API Docs.
Has this interface been replaced by something else?
The original code:
import io.github.jhipster.config.JHipsterConstants;
import io.github.jhipster.config.JHipsterProperties;
import io.github.jhipster.web.filter.CachingHttpHeadersFilter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.servlet.InstrumentedFilter;
import com.codahale.metrics.servlets.MetricsServlet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.embedded.*;
import org.springframework.boot.web.embedded.undertow.*;//.UndertowEmbeddedServletContainerFactory;
import io.undertow.UndertowOptions;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import java.io.File;
import java.nio.file.Paths;
import java.util.*;
import javax.servlet.*;
/**
* Configuration of web application with Servlet 3.0 APIs.
*/
#Configuration
public class WebConfigurer implements ServletContextInitializer, EmbeddedServletContainerCustomizer {
private final Logger log = LoggerFactory.getLogger(WebConfigurer.class);
private final Environment env;
private final JHipsterProperties jHipsterProperties;
private MetricRegistry metricRegistry;
public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) {
this.env = env;
this.jHipsterProperties = jHipsterProperties;
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
if (env.getActiveProfiles().length != 0) {
log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles());
}
EnumSet<DispatcherType> disps = EnumSet.of(DispatcherType.REQUEST, DispatcherType.FORWARD, DispatcherType.ASYNC);
initMetrics(servletContext, disps);
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) {
initCachingHttpHeadersFilter(servletContext, disps);
}
if (env.acceptsProfiles(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT)) {
initH2Console(servletContext);
}
log.info("Web application fully configured");
}
/**
* Customize the Servlet engine: Mime types, the document root, the cache.
*/
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
MimeMappings mappings = new MimeMappings(MimeMappings.DEFAULT);
// IE issue, see https://github.com/jhipster/generator-jhipster/pull/711
mappings.add("html", "text/html;charset=utf-8");
// CloudFoundry issue, see https://github.com/cloudfoundry/gorouter/issues/64
mappings.add("json", "text/html;charset=utf-8");
container.setMimeMappings(mappings);
// When running in an IDE or with ./gradlew bootRun, set location of the static web assets.
setLocationForStaticAssets(container);
/*
* Enable HTTP/2 for Undertow - https://twitter.com/ankinson/status/829256167700492288
* HTTP/2 requires HTTPS, so HTTP requests will fallback to HTTP/1.1.
* See the JHipsterProperties class and your application-*.yml configuration files
* for more information.
*/
if (jHipsterProperties.getHttp().getVersion().equals(JHipsterProperties.Http.Version.V_2_0) &&
container instanceof UndertowEmbeddedServletContainerFactory) {
((UndertowEmbeddedServletContainerFactory) container)
.addBuilderCustomizers(builder ->
builder.setServerOption(UndertowOptions.ENABLE_HTTP2, true));
}
}
private void setLocationForStaticAssets(ConfigurableEmbeddedServletContainer container) {
File root;
String prefixPath = resolvePathPrefix();
root = new File(prefixPath + "build/www/");
if (root.exists() && root.isDirectory()) {
container.setDocumentRoot(root);
}
}
/**
* Resolve path prefix to static resources.
*/
private String resolvePathPrefix() {
String fullExecutablePath = this.getClass().getResource("").getPath();
String rootPath = Paths.get(".").toUri().normalize().getPath();
String extractedPath = fullExecutablePath.replace(rootPath, "");
int extractionEndIndex = extractedPath.indexOf("build/");
if(extractionEndIndex <= 0) {
return "";
}
return extractedPath.substring(0, extractionEndIndex);
}
/**
* Initializes the caching HTTP Headers Filter.
*/
private void initCachingHttpHeadersFilter(ServletContext servletContext,
EnumSet<DispatcherType> disps) {
log.debug("Registering Caching HTTP Headers Filter");
FilterRegistration.Dynamic cachingHttpHeadersFilter =
servletContext.addFilter("cachingHttpHeadersFilter",
new CachingHttpHeadersFilter(jHipsterProperties));
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/content/*");
cachingHttpHeadersFilter.addMappingForUrlPatterns(disps, true, "/app/*");
cachingHttpHeadersFilter.setAsyncSupported(true);
}
/**
* Initializes Metrics.
*/
private void initMetrics(ServletContext servletContext, EnumSet<DispatcherType> disps) {
log.debug("Initializing Metrics registries");
servletContext.setAttribute(InstrumentedFilter.REGISTRY_ATTRIBUTE,
metricRegistry);
servletContext.setAttribute(MetricsServlet.METRICS_REGISTRY,
metricRegistry);
log.debug("Registering Metrics Filter");
FilterRegistration.Dynamic metricsFilter = servletContext.addFilter("webappMetricsFilter",
new InstrumentedFilter());
metricsFilter.addMappingForUrlPatterns(disps, true, "/*");
metricsFilter.setAsyncSupported(true);
log.debug("Registering Metrics Servlet");
ServletRegistration.Dynamic metricsAdminServlet =
servletContext.addServlet("metricsServlet", new MetricsServlet());
metricsAdminServlet.addMapping("/management/metrics/*");
metricsAdminServlet.setAsyncSupported(true);
metricsAdminServlet.setLoadOnStartup(2);
}
#Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = jHipsterProperties.getCors();
if (config.getAllowedOrigins() != null && !config.getAllowedOrigins().isEmpty()) {
log.debug("Registering CORS filter");
source.registerCorsConfiguration("/api/**", config);
source.registerCorsConfiguration("/v2/api-docs", config);
}
return new CorsFilter(source);
}
/**
* Initializes H2 console.
*/
private void initH2Console(ServletContext servletContext) {
log.debug("Initialize H2 console");
ServletRegistration.Dynamic h2ConsoleServlet = servletContext.addServlet("H2Console", new org.h2.server.web.WebServlet());
h2ConsoleServlet.addMapping("/h2-console/*");
h2ConsoleServlet.setInitParameter("-properties", "src/main/resources/");
h2ConsoleServlet.setLoadOnStartup(1);
}
#Autowired(required = false)
public void setMetricRegistry(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
}
}
UndertowEmbeddedServletContainerFactory has been renamed in 2.0. It's now UndertowServletWebServerFactory. I noticed that you're also using EmbeddedServletContainerCustomizer. Its equivalent in 2.0 is org.springframework.boot.web.server.WebServerFactoryCustomizer.

Set SSL and timeout to WS call in Spring (HttpsUrlConnectionMessageSender, HttpsUrlConnectionMessageSender)

I am trying to set timeout and SSL (https) for WS call:
PS: No need to mark this as duplicated, the only similar question has never been answered.
I tried HttpsUrlConnectionMessageSender that adds support for (self-signed) HTTPS certificates but it does support timeout.
when I switch to HttpComponentsMessageSender that supports timeout (Connection and read timeouts) it does support SSL.
I want to combile timeout and ssl to when calling WS:
webServiceTemplate.setDefaultUri(uri);
response = webServiceTemplate.marshalSendAndReceive(inputs, new SoapHandler(createCredentials(), soapAction));
Finally, did it using HttpComponentsMessageSender. Here is my code:
HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender();
HttpClient httpClient = HttpClientFactory.getHttpsClient(sslUtils, timeout);
messageSender.setHttpClient(httpClient);
webServiceTemplate.setMessageSender(messageSender);
I also created a new factory class HttpClientFactory that sets the SSL and timeout:
import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.protocol.HttpContext;
public class HttpClientFactory {
private static CloseableHttpClient client;
private HttpClientFactory() {
}
public static HttpClient getHttpsClient(SslUtils sslUtils, int timeout) throws Exception {
if (client != null) {
return client;
}
SSLContext sslcontext = getSSLContext(sslUtils);
SSLConnectionSocketFactory factory = new SSLConnectionSocketFactory(sslcontext, new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
HttpClientBuilder httpClientBuilder = HttpClients.custom();
httpClientBuilder.addInterceptorFirst(new ContentLengthHeaderRemover());
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(timeout)
.setConnectionRequestTimeout(timeout)
.setSocketTimeout(timeout)
.build();
return httpClientBuilder.setSSLSocketFactory(factory)
.setDefaultRequestConfig(config)
.build();
}
private static class ContentLengthHeaderRemover implements HttpRequestInterceptor {
#Override
public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
request.removeHeaders(HTTP.CONTENT_LEN);
}
}
public static void releaseInstance() {
client = null;
}
private static SSLContext getSSLContext(SslUtils sslUtils) throws KeyStoreException, NoSuchAlgorithmException, CertificateException, IOException, KeyManagementException {
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(sslUtils.getKeystore().getInputStream(), sslUtils.getKeyPwd().toCharArray());
sslUtils.getKeystore().getInputStream().close();
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(sslUtils.getTrustStore().getInputStream(), sslUtils.getTrustPwd().toCharArray());
sslUtils.getTrustStore().getInputStream().close();
SSLContextBuilder sslContextBuilder = SSLContexts.custom();
try {
sslContextBuilder = SSLContexts.custom().loadKeyMaterial(ks, ssl.getKeyPwd().toCharArray());
} catch (UnrecoverableKeyException e) {
e.printStack();
}
sslContextBuilder.loadTrustMaterial(ts, new TrustSelfSignedStrategy());
return sslContextBuilder.build();
}
}
For information the SslUtils is just a bean class that holds the keystore and truststore informations' :
public class SslUtils {
private Resource keystore;
private String keyPwd;
private Resource trustStore;
private String trustPwd;
// Getters and Setters
}
This works for me and let me use both SSL and timeout at the same. I hope this will help others.
In a case of HTTPS protocol with basic authentication, you may not need a certificate, you can set the encoded username:password into the header of the request
package com.james.medici.app.ws;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.ws.client.core.WebServiceTemplate;
import org.springframework.ws.transport.http.HttpUrlConnectionMessageSender;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.util.Base64;
#Slf4j
#Configuration
public class SoapClientConfiguration {
#Value("${james.medici.url}")
private String defaultUri;
#Value("${james.medici.username}")
private String userName;
#Value("${james.medici.passcode}")
private String userPassword;
public static final String SEPARATOR = ":";
public static final String AUTHORIZATION = "Authorization";
public static final String BASIC = "Basic ";
class CustomHttpUrlConnectionMessageSender extends HttpUrlConnectionMessageSender {
#Override
protected void prepareConnection(HttpURLConnection connection) throws IOException {
Base64.Encoder enc = Base64.getEncoder();
String userpassword = StringUtils.joinWith(SEPARATOR, userName, userPassword);
String encodedAuthorization = enc.encodeToString(userpassword.getBytes());
connection.setRequestProperty(AUTHORIZATION, BASIC + encodedAuthorization);
super.prepareConnection(connection);
}
}
#Bean
public Jaxb2Marshaller marshaller() {
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setContextPath("com.james.medici.app.ws.model");
return marshaller;
}
#Bean
public WebServiceTemplate webServiceTemplate() {
log.info(defaultUri);
WebServiceTemplate webServiceTemplate = new WebServiceTemplate();
webServiceTemplate.setMarshaller(marshaller());
webServiceTemplate.setUnmarshaller(marshaller());
webServiceTemplate.setDefaultUri(defaultUri);
webServiceTemplate.setMessageSender(new CustomHttpUrlConnectionMessageSender());
return webServiceTemplate;
}
}

Resources