Too many connection for Spring Boot Application and Apache storm - spring

I am using spring boot application for websocket and in my storm project i am calling the spring boot application everytime to publish real time sensor data i,e per 5 minutes it is establishing 5000 connection so how to avoid this problem?
Is there any way to do connection pooling for spring boot connection in the Apache storm project?
String boot application controller class:
private static Logger logger=Logger.getLogger(GreetingController.class);
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
//logger.info("+++++++Sending Greetinge+++++++" + message);
//Thread.sleep(3000); // simulated delay
return new Greeting("Hello greetings, " + message.getName() + "!");
}
#MessageMapping("/orignal")
#SendTo("/topic/orignal")
public Map<String, Object> orignal(Map<String, Object> orignal) throws Exception {
// logger.info("+++++++Sending orignale+++++++" + orignal);
//System.out.println("Sending orignal");
//Thread.sleep(3000); // simulated delay
return orignal;
}
#MessageMapping("/tupple")
#SendTo("/topic/tupple")
public Map<String, Object> tupple(Map<String, Object> tupple) throws Exception {
logger.info("+++++++Sending tupple+++++++" + tupple);
//System.out.println("Sending tupple");
//Thread.sleep(3000); // simulated delay
//String company_name=(String) tupple.get("company_name");
/*Map<String, Object> companyMap=new HashMap<String, Object>();
companyMap.put("success", true);
companyMap.put("message", "company created successfully");*/
return tupple;
}
#MessageMapping("/websocket")
#SendTo("/topic/websocket")
public Map<String, Object> websocket(Map<String, Object> websocket) throws Exception {
logger.info("+++++++Sending websocket+++++++" + websocket);
//System.out.println("Sending websocket");
//Thread.sleep(3000); // simulated delay
return websocket;
}
In storm project making connection to spring boot application:
private final static WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
public ListenableFuture<StompSession> connect(String endpoint) {
Transport webSocketTransport = new WebSocketTransport(new StandardWebSocketClient());
List<Transport> transports = Collections.singletonList(webSocketTransport);
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
/*String url = "ws://{host}:{port}/"+endpoint;
return stompClient.connect(url, headers, new MyHandler(), "10.0.0.6", 8080);*/
String url = "ws://*************/IoTWebSocketServer-1.2.5.RELEASE/"+endpoint;
return stompClient.connect(url, headers, new MyHandler());
}
public void subscribeGreetings(String topic,StompSession stompSession) throws ExecutionException, InterruptedException {
stompSession.subscribe(topic, new StompFrameHandler() {
public Type getPayloadType(StompHeaders stompHeaders) {
return byte[].class;
}
public void handleFrame(StompHeaders stompHeaders, Object o) {
}
});
}
public void sendHello(StompSession stompSession,String message) {
String jsonHello = "{ \"name\" : \""+message+"\" }";
stompSession.send("/app/hello", jsonHello.getBytes());
}
private class MyHandler extends StompSessionHandlerAdapter {
public void afterConnected(StompSession stompSession, StompHeaders stompHeaders) {
}
}
public static void main(String[] args) throws Exception {
HelloClient helloClient = new HelloClient();
String endPoint = "hello";
ListenableFuture<StompSession> f = helloClient.connect(endPoint);
StompSession stompSession = f.get();
String topic= "/topic/greetings";
helloClient.subscribeGreetings(topic,stompSession);
String message = "hieeeeeeeeeeeeeeee message";
helloClient.sendHello(stompSession,message);
HelloClient helloClienttupple = new HelloClient();
String endPointtupple = "tupple";
ListenableFuture<StompSession> ftupple = helloClient.connect(endPointtupple);
StompSession stompSessiontupple = ftupple.get();
String topictupple= "/topic/tupple";
helloClient.subscribeGreetings(topictupple,stompSessiontupple);
String messagetupple = "hieeeeeeeeeeeeeeee tupple";
helloClienttupple.sendHello(stompSessiontupple,messagetupple);
JSONObject companyMap = new JSONObject();
companyMap.put("name", "Mahabali");
companyMap.put("num", new Integer(100));
companyMap.put("balance", new Double(1000.21));
companyMap.put("is_vip", new Boolean(true));
System.out.println(companyMap.toString());
stompSessiontupple.send("/app/tupple", companyMap.toString().getBytes());
//Thread.sleep(60000);
}
This the connection establishing code:
t
ry{
logger.info("++++Websocket Gateway packet++++" + topicId);
JSONObject jsonObject=new JSONObject();
JSONObject jsonObject1=new JSONObject();
JSONArray array=new JSONArray();
jsonObject.put("MAC_IMEI", topicId);
jsonObject.put("TIME", eventTime);
jsonObject.put("DEVICE", "GATEWAY");
jsonObject.put("GATEWAY", nJsonArray);
array.put(jsonObject);
jsonObject1.put("GATEWAY", array);
HelloClient helloClientwebsocket = new HelloClient();
String endPointwebsocket = "websocket";
ListenableFuture<StompSession> fwebsocket = helloClientwebsocket.connect(endPointwebsocket);
StompSession stompSessionwebsocket = fwebsocket.get();
String topicwebsocket= "/topic/websocket";
helloClientwebsocket.subscribeGreetings(topicwebsocket,stompSessionwebsocket);
stompSessionwebsocket.send("/app/websocket", jsonObject.toString().getBytes());
}
catch(Exception e)
{
logger.info("Gateway exception");
logger.error("++++Exception caught during sending the sensor details in the new websocket server++++"+e);
}

Related

Spring Stomp slow connection

I'm experiencing a slow Stomp connection between two Spring microservices, the client takes about 5 minutes to send the CONNECT Stomp message. Both microservices run on the same node.
Spring Boot version: 2.1.3.RELEASE
The Websocket server is configured in the following way:
#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-subscribe").setAllowedOrigins("*").withSockJS();
}
}
The client is configured this way:
#Component
public class WebsocketClient {
private GlobalConfiguration globalConfiguration;
private WebsocketListener websocketListener;
private WebSocketStompClient stompClient;
#Autowired
private JwtTokenGenerator jwtTokenGenerator;
private ApplicationContext appContext;
private Logger LOGGER = LoggerFactory.getLogger(WebsocketClient.class);
#Autowired
public WebsocketClient(ApplicationContext context,GlobalConfiguration globalConfiguration) {
Transport webSocketTransport = new WebSocketTransport(new StandardWebSocketClient());
List<Transport> transports = Collections.singletonList(webSocketTransport);
SockJsClient sockJsClient = new SockJsClient(transports);
sockJsClient.setMessageCodec(new Jackson2SockJsMessageCodec());
this.stompClient = new WebSocketStompClient(sockJsClient);
this.globalConfiguration = globalConfiguration;
this.websocketListener = new WebsocketListener(context);
}
public ListenableFuture<StompSession> connectToWebsocket(){
String url = globalConfiguration.getOrchestratorWs();
StompHeaders connectHeaders = new StompHeaders();
WebSocketHttpHeaders webSocketHttpHeaders = new WebSocketHttpHeaders();
webSocketHttpHeaders.add("Authorization",jwtTokenGenerator.token);
LOGGER.info("webSocketHttpHeaders: " + webSocketHttpHeaders.toString());
LOGGER.info("url: " + url);
this.stompClient.setMessageConverter(new MappingJackson2MessageConverter());
return stompClient.connect(url,webSocketHttpHeaders,connectHeaders, websocketListener);
}
}
And client-side we have this Websocket listener:
public class WebsocketListener extends StompSessionHandlerAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(WebsocketListener.class);
private GlobalConfiguration globalConfiguration;
private ApplicationContext appContext;
public WebsocketListener(ApplicationContext appContext) {
this.appContext = appContext;
this.globalConfiguration = appContext.getBean(GlobalConfiguration.class);
}
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
LOGGER.info("New session established : " + globalConfiguration.getId());
session.subscribe("/topic/injections/"+globalConfiguration.getId(), this);
LOGGER.info("Subscribed to /topic/injections/"+globalConfiguration.getId());
// session.send("/app/chat", getSampleMessage());
// logger.info("Message sent to websocket server");
}
#Override
public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {
LOGGER.error("Got an exception", exception);
}
#Override
public Type getPayloadType(StompHeaders headers) {
return Map.class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
LOGGER.info("Injection Received : " + payload);
InjectionHandler injectionHandler = new InjectionHandler(appContext,(HashMap) payload);
injectionHandler.start();
}
}
The afterConnected is invoked about 5 minutes after the ws://10.2.0.43:7071/ws-subscribe/513/8306a7ac357847678795b049450fb6c5/websocket
Inspecting the source code of the Stomp library, I saw that the code where the microservice remains stuck for those 5 minutes is the following:
public ListenableFuture<StompSession> connect(URI url, #Nullable WebSocketHttpHeaders handshakeHeaders, #Nullable StompHeaders connectHeaders, StompSessionHandler sessionHandler) {
Assert.notNull(url, "'url' must not be null");
ConnectionHandlingStompSession session = this.createSession(connectHeaders, sessionHandler);
WebSocketStompClient.WebSocketTcpConnectionHandlerAdapter adapter = new WebSocketStompClient.WebSocketTcpConnectionHandlerAdapter(session);
this.getWebSocketClient().doHandshake(adapter, handshakeHeaders, url).addCallback(adapter);
return session.getSessionFuture();
}
In particular, the session.getSessionFuture() call.
Any idea on what may cause the delay in the CONNECT message sent by the client?

Getting TestRestTemplate to work with https

Writing JUnit Integrtaion tests for a REST endpoint which sets secure cookies, can't get past the ResourceAccessException error.
Requirement is to do a https://localhost:8443 request.
Have tried using the customRestTemplate
Getting the folloiwng exception.
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8443/dcs": Connect to localhost:8443 [localhost/127.0.0.1, localhost/0:0:0:0:0:0:0:1] failed: Connection refused: connect; nested exception is org.apache.http.conn.HttpHostConnectException
Below is the code.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class DcsServiceTests {
#Autowired
RestTemplateBuilder restTemplateBuilder;
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testGet_ImageResponse() throws Exception {
//Arrange
//Act
ResponseEntity<byte[]> response = testRestTemplate.getForEntity(url, byte[].class);
//Assert
//Response Status
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
//Response has cookie
assertThat(response.getHeaders().containsKey("Set-Cookie")).isTrue();
}
#PostConstruct
public void initialize() {
// Lambda expression not working, TBD - Java version used.
//TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
final TrustStrategy acceptingTrustStrategy = new TrustStrategy() {
#Override
public boolean isTrusted(java.security.cert.X509Certificate[] arg0, String arg1)
throws CertificateException {
return true;
}
};
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
try {
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
.loadTrustMaterial(null, acceptingTrustStrategy)
.build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
requestFactory.setHttpClient(httpClient);
}
catch (Exception e) {
System.out.println("Exception occured creating Request Factory");
}
RestTemplate customTemplate = restTemplateBuilder
.requestFactory(requestFactory)
.rootUri("https://localhost:8443")
.build();
this.testRestTemplate = new TestRestTemplate(
customTemplate,
null,
null, // Not using basic auth
TestRestTemplate.HttpClientOption.ENABLE_COOKIES); // Cookie support
}
}
Disabling SSL and then using testRestTemplate with exchange method worked. Secured cookies works as well, just that the headers needs to be parsed to validate results in Unit test cases
#Bean
public Boolean disableSSLValidation() throws Exception {
final SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { new X509TrustManager() {
#Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException {
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
} }, null);
HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
return true;
}
public void hostNameVerifier() {
final HostnameVerifier defaultHostnameVerifier = javax.net.ssl.HttpsURLConnection.getDefaultHostnameVerifier ();
final HostnameVerifier localhostAcceptedHostnameVerifier = new javax.net.ssl.HostnameVerifier () {
public boolean verify ( String hostname, javax.net.ssl.SSLSession sslSession ) {
if ( hostname.equals ( "localhost" ) ) {
return true;
}
return defaultHostnameVerifier.verify ( hostname, sslSession );
}
};
javax.net.ssl.HttpsURLConnection.setDefaultHostnameVerifier ( localhostAcceptedHostnameVerifier );
}
#Test
public void testGet_ImageResponse() throws Exception {
//Arrange
String url = getUrl() + "/xyz?s_action=test&s_type=i";
//Act
ResponseEntity<byte[]> response = restTemplate.getForEntity(url, byte[].class);
//Assert
//Response Status
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
//Response has cookie
assertThat(response.getHeaders().containsKey("Set-Cookie")).isTrue();
//Extract cookie from header
List<String> cookies = response.getHeaders().get("Set-Cookie");
//Construct cookie from RAW Header Response
Cookie cookie = RawCookieParser.constructCookieFromHeaderResponse(response.getHeaders().get("Set-Cookie").toString());
//Cookies name matches
//Cookie value cannot be matched because value is being set from external JAR
assertEquals(cookie.getName(), appConfig.getName());
//Cookie domain matches
assertEquals(cookie.getDomain(), appConfig.getDomain());
}
public class RawCookieParser {
/*
* Construct a cookie object by parsing the HTTP Header response
*/
public static Cookie constructCookieFromHeaderResponse(String input) throws Exception {
String rawCookie = input.replace("[", "").replace("]", "");
String[] rawCookieParams = rawCookie.split(";");
String[] rawCookieNameAndValue = rawCookieParams[0].split("=");
if (rawCookieNameAndValue.length != 2) {
throw new Exception("Invalid cookie: missing name and value.");
}
String cookieName = rawCookieNameAndValue[0].trim();
String cookieValue = rawCookieNameAndValue[1].trim();
Cookie cookie = new Cookie(cookieName, cookieValue);
for (int i = 1; i < rawCookieParams.length; i++) {
String rawCookieParamNameAndValue[] = rawCookieParams[i].trim().split("=");
String paramName = rawCookieParamNameAndValue[0].trim();
if (rawCookieParamNameAndValue.length == 2) {
String paramValue = rawCookieParamNameAndValue[1].trim();
if (paramName.equalsIgnoreCase("secure")) {
cookie.setSecure(true);
} else if (paramName.equalsIgnoreCase("max-age")) {
int maxAge = Integer.parseInt(paramValue);
cookie.setMaxAge(maxAge);
} else if (paramName.equalsIgnoreCase("domain")) {
cookie.setDomain(paramValue);
} else if (paramName.equalsIgnoreCase("path")) {
cookie.setPath(paramValue);
}
}
}
return cookie;
}
}

Convert to multiple client server Connection

making my previous question more readable, following is my code which works fine for single server single client connection, but i want my client to connect 2 or more servers dynamically,
public class ClientCall {
public static void main(String[] args) {
#SuppressWarnings("resource")
ApplicationContext ctx = new AnnotationConfigApplicationContext(GatewayConfig.class);
GatewayService gatewayService = ctx.getBean(GatewayService.class);
//int i=0;
Message message = new Message();
/*while(i<4)
{*/
message.setPayload("It's working");
gatewayService.sendMessage(message);
/* i++;
}*/
}
}
public class Message {
private String payload;
// getter setter
}
#EnableIntegration
#IntegrationComponentScan
#Configuration
#ComponentScan(basePackages = "com.gateway.service")
public class GatewayConfig {
// #Value("${listen.port:6788}")
private int port = 6785;
#Autowired
private GatewayService<Message> gatewayService;
#MessagingGateway(defaultRequestChannel = "sendMessageChannel")
public interface Gateway {
void viaTcp(String payload);
}
#Bean
public AbstractClientConnectionFactory clientCF() {
TcpNetClientConnectionFactory clientConnectionFactory = new TcpNetClientConnectionFactory("localhost",
this.port);
clientConnectionFactory.setSingleUse(false);
return clientConnectionFactory;
}
#Bean
#ServiceActivator(inputChannel = "sendMessageChannel")
public MessageHandler tcpOutGateway(AbstractClientConnectionFactory connectionFactory) {
TcpOutboundGateway outGateway = new TcpOutboundGateway();
outGateway.setConnectionFactory(connectionFactory);
// outGateway.setAsync(true);
outGateway.setOutputChannel(receiveMessageChannel());
outGateway.setRequiresReply(true);
outGateway.setReplyChannel(receiveMessageChannel());
return outGateway;
}
#Bean
public MessageChannel sendMessageChannel() {
DirectChannel channel = new DirectChannel();
return channel;
}
#Bean
public MessageChannel receiveMessageChannel() {
DirectChannel channel = new DirectChannel();
return channel;
}
#Transformer(inputChannel = "receiveMessageChannel", outputChannel = "processMessageChannel")
public String convert(byte[] bytes) {
return new String(bytes);
}
#ServiceActivator(inputChannel = "processMessageChannel")
public void upCase(String response) {
gatewayService.receiveMessage(response);
}
#Transformer(inputChannel = "errorChannel", outputChannel = "processMessageChannel")
public void convertError(byte[] bytes) {
String str = new String(bytes);
System.out.println("Error: " + str);
}
}
public interface GatewayService<T> {
public void sendMessage(final T payload);
public void receiveMessage(String response);
}
#Service
public class GatewayServiceImpl implements GatewayService<Message> {
#Autowired
private Gateway gateway;
#Autowired
private GatewayContextManger<String, Object> gatewayContextManger;
#Override
public void sendMessage(final Message message) {
new Thread(new Runnable() {
#Override
public void run() {
gateway.viaTcp(message.getPayload());
}
}).start();
}
#Override
public void receiveMessage(final String response) {
new Thread(new Runnable() {
#Override
public void run() {
Message message = new Message();
message.setPayload(response);
Object obj = gatewayContextManger.get(message.getPayload());
synchronized (obj) {
obj.notify();
}
}
}).start();
}
}
and below is sever code similar there is another server with different port and ip then how to make connections to these servers?
class TCPServer
{
public static void main(String argv[]) throws Exception
{
String clientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6785);
while(true)
{
Socket connectionSocket = welcomeSocket.accept();
BufferedReader inFromClient =
new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
clientSentence = inFromClient.readLine();
System.out.println("Received: " + clientSentence);
capitalizedSentence = clientSentence + "\r\n";
outToClient.writeBytes(capitalizedSentence);
}
}
}
A few comments.
Instead of starting a thread to send the message, simply make sendMessageChannel an ExecutorChannel, using a ThreadPoolTaskExecutor - it will be more efficient and gets you out of the business of managing threads.
If you only have 2 servers to connect to, rather than coming up with a dynamic scheme, simply define 2 TCP adapters and add a #Router after sendMessageChannel.
You can tell the router which server to send it to by setting a header.
#MessagingGateway(defaultRequestChannel = "sendMessageChannel")
public interface Gateway {
void viaTcp(String payload #Header("which") String target);
}
Use a HeaderValueRouter to route on header which.
See Message Routing in the reference manual.

java.io.IOException With Restful Web Service

I have a weird problem with my project. When i run main method in Client to create a User after send to Server then occur fllowing error:
Exception in thread "main" java.io.IOException: Server returned HTTP
response code: 400 for URL: http://localhost:8080/rest/user/create at
sun.net.www.protocol.http.HttpURLConnection.getInputStream0(Unknown
Source) at
sun.net.www.protocol.http.HttpURLConnection.getInputStream(Unknown
Source) at
edu.java.spring.service.client.RestClientTest.createUser(RestClientTest.java:72)
at
edu.java.spring.service.client.RestClientTest.main(RestClientTest.java:30)
Here file RestClientTest.java
public class RestClientTest {
public static void main(String[] args) throws IOException{
// System.out.println("Rest Response" + loadUser("quypham"));
User user = new User();
user.setUserName("datpham");
user.setPassWord("12345");
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR,1960);
user.setBirthDay(calendar.getTime());
user.setAge(12);
createUser(user);
System.out.println("Rest Response" + loadUser("datpham"));
}
public static String createUser(User user) throws IOException{
URL url = new URL("http://localhost:8080/rest/user/create");
HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection();
httpConnection.setRequestMethod("POST");
httpConnection.addRequestProperty("Content-Type","application/json");
httpConnection.addRequestProperty("Accept","application/json");
httpConnection.setDoOutput(true);
DataOutputStream wr = new DataOutputStream(httpConnection.getOutputStream());
ObjectMapper mapper = new ObjectMapper();
StringWriter writer = new StringWriter();
mapper.writeValue(writer,user);
wr.writeBytes(writer.getBuffer().toString());
wr.flush();
wr.close();
return readInputStream(httpConnection.getInputStream());
}
public static String readInputStream(InputStream stream) throws IOException{
StringWriter writer = new StringWriter();
try {
int read;
byte[] bytes = new byte[4*1024];
while ((read = stream.read(bytes))!=-1) {
writer.write(new String(bytes,0,read));
}
} finally {
// TODO: handle finally clause
stream.close();
writer.close();
}
return writer.getBuffer().toString();
}
Here file UserRestServiceController.java
#Controller
public class UserRestServiceController {
#Autowired
public UserDao userDao;
#RequestMapping(value = "/rest/user/create",method = RequestMethod.POST,consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.CREATED)
public void addUser(#RequestBody User user){
userDao.save(user);
}

Access Https Rest Service using Spring RestTemplate

Can anybody provide me with a code sample to access the rest service URL secured with HTTPS using the Spring Rest template?
I have the certificate, username and password. Basic Authentication is used on the server-side and I want to create a client that can connect to that server using a provided certificate, username and password (if needed).
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(new File(keyStoreFile)),
keyStorePassword.toCharArray());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
new SSLContextBuilder()
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.loadKeyMaterial(keyStore, keyStorePassword.toCharArray())
.build(),
NoopHostnameVerifier.INSTANCE);
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(
socketFactory).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(
httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
MyRecord record = restTemplate.getForObject(uri, MyRecord.class);
LOG.debug(record.toString());
Here is some code that will give you the general idea.
You need to create a custom ClientHttpRequestFactory in order to trust the certificate.
It looks like this:
final ClientHttpRequestFactory clientHttpRequestFactory =
new MyCustomClientHttpRequestFactory(org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER, serverInfo);
restTemplate.setRequestFactory(clientHttpRequestFactory);
This is the implementation for MyCustomClientHttpRequestFactory:
public class MyCustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
private final HostnameVerifier hostNameVerifier;
private final ServerInfo serverInfo;
public MyCustomClientHttpRequestFactory (final HostnameVerifier hostNameVerifier,
final ServerInfo serverInfo) {
this.hostNameVerifier = hostNameVerifier;
this.serverInfo = serverInfo;
}
#Override
protected void prepareConnection(final HttpURLConnection connection, final String httpMethod)
throws IOException {
if (connection instanceof HttpsURLConnection) {
((HttpsURLConnection) connection).setHostnameVerifier(hostNameVerifier);
((HttpsURLConnection) connection).setSSLSocketFactory(initSSLContext()
.getSocketFactory());
}
super.prepareConnection(connection, httpMethod);
}
private SSLContext initSSLContext() {
try {
System.setProperty("https.protocols", "TLSv1");
// Set ssl trust manager. Verify against our server thumbprint
final SSLContext ctx = SSLContext.getInstance("TLSv1");
final SslThumbprintVerifier verifier = new SslThumbprintVerifier(serverInfo);
final ThumbprintTrustManager thumbPrintTrustManager =
new ThumbprintTrustManager(null, verifier);
ctx.init(null, new TrustManager[] { thumbPrintTrustManager }, null);
return ctx;
} catch (final Exception ex) {
LOGGER.error(
"An exception was thrown while trying to initialize HTTP security manager.", ex);
return null;
}
}
In this case my serverInfo object contains the thumbprint of the server.
You need to implement the TrustManager interface to get
the SslThumbprintVerifier or any other method you want to verify your certificate (you can also decide to also always return true).
The value org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER allows all host names.
If you need to verify the host name,
you will need to implement it differently.
I'm not sure about the user and password and how you implemented it.
Often,
you need to add a header to the restTemplate named Authorization
with a value that looks like this: Base: <encoded user+password>.
The user+password must be Base64 encoded.
This is a solution with no deprecated class or method :
(Java 8 approved)
CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
Important information : Using NoopHostnameVerifier is a security risk
One point from me. I used a mutual cert authentication with spring-boot microservices. The following is working for me, key points here are
keyManagerFactory.init(...) and sslcontext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom()) lines of code without them, at least for me, things did not work. Certificates are packaged by PKCS12.
#Value("${server.ssl.key-store-password}")
private String keyStorePassword;
#Value("${server.ssl.key-store-type}")
private String keyStoreType;
#Value("${server.ssl.key-store}")
private Resource resource;
private RestTemplate getRestTemplate() throws Exception {
return new RestTemplate(clientHttpRequestFactory());
}
private ClientHttpRequestFactory clientHttpRequestFactory() throws Exception {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
private HttpClient httpClient() throws Exception {
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
KeyStore trustStore = KeyStore.getInstance(keyStoreType);
if (resource.exists()) {
InputStream inputStream = resource.getInputStream();
try {
if (inputStream != null) {
trustStore.load(inputStream, keyStorePassword.toCharArray());
keyManagerFactory.init(trustStore, keyStorePassword.toCharArray());
}
} finally {
if (inputStream != null) {
inputStream.close();
}
}
} else {
throw new RuntimeException("Cannot find resource: " + resource.getFilename());
}
SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build();
sslcontext.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
SSLConnectionSocketFactory sslConnectionSocketFactory =
new SSLConnectionSocketFactory(sslcontext, new String[]{"TLSv1.2"}, null, getDefaultHostnameVerifier());
return HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
}
Here is what I ended up with for the similar problem. The idea is the same as in #Avi's answer, but I also wanted to avoid the static "System.setProperty("https.protocols", "TLSv1");", so that any adjustments won't affect the system. Inspired by an answer from here http://www.coderanch.com/t/637177/Security/Disabling-handshake-message-Java
public class MyCustomClientHttpRequestFactory extends SimpleClientHttpRequestFactory {
#Override
protected void prepareConnection(HttpURLConnection connection, String httpMethod) {
try {
if (!(connection instanceof HttpsURLConnection)) {
throw new RuntimeException("An instance of HttpsURLConnection is expected");
}
HttpsURLConnection httpsConnection = (HttpsURLConnection) connection;
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
}
};
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory()));
httpsConnection.setHostnameVerifier((hostname, session) -> true);
super.prepareConnection(httpsConnection, httpMethod);
} catch (Exception e) {
throw Throwables.propagate(e);
}
}
/**
* We need to invoke sslSocket.setEnabledProtocols(new String[] {"SSLv3"});
* see http://www.oracle.com/technetwork/java/javase/documentation/cve-2014-3566-2342133.html (Java 8 section)
*/
private static class MyCustomSSLSocketFactory extends SSLSocketFactory {
private final SSLSocketFactory delegate;
public MyCustomSSLSocketFactory(SSLSocketFactory delegate) {
this.delegate = delegate;
}
#Override
public String[] getDefaultCipherSuites() {
return delegate.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites() {
return delegate.getSupportedCipherSuites();
}
#Override
public Socket createSocket(final Socket socket, final String host, final int port, final boolean autoClose) throws IOException {
final Socket underlyingSocket = delegate.createSocket(socket, host, port, autoClose);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final String host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final InetAddress host, final int port) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port);
return overrideProtocol(underlyingSocket);
}
#Override
public Socket createSocket(final InetAddress host, final int port, final InetAddress localAddress, final int localPort) throws IOException {
final Socket underlyingSocket = delegate.createSocket(host, port, localAddress, localPort);
return overrideProtocol(underlyingSocket);
}
private Socket overrideProtocol(final Socket socket) {
if (!(socket instanceof SSLSocket)) {
throw new RuntimeException("An instance of SSLSocket is expected");
}
((SSLSocket) socket).setEnabledProtocols(new String[] {"SSLv3"});
return socket;
}
}
}
You need to configure a raw HttpClient with SSL support, something like this:
#Test
public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect()
throws ClientProtocolException, IOException {
CloseableHttpClient httpClient
= HttpClients.custom()
.setSSLHostnameVerifier(new NoopHostnameVerifier())
.build();
HttpComponentsClientHttpRequestFactory requestFactory
= new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
ResponseEntity<String> response
= new RestTemplate(requestFactory).exchange(
urlOverHttps, HttpMethod.GET, null, String.class);
assertThat(response.getStatusCode().value(), equalTo(200));
}
from: Baeldung

Resources