Call https endpoints in spring boot - spring-boot

How to call https endpoint with httpclient5. I cant use this way
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext);
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();

Related

Spring Boot WebClient with OAuth2 and use InsecureTrustManagerFactory

I have successfully implemented WebClient with oAuth2. Facing problem with oAuth2 when the Authentication Server (Keycloak) is having SSL (https). Though I am passing InsecureTrustManagerFactory while defining WebClient, this oAuth is called before the builder is complete as it is there in the filter, it uses default implementation of WebClient and throws certification error.
Is there a way we can configure oAuth2 client also to use InsecureTrustManagerFactory?
pom.xml part
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
Bean Configuration
#Bean
public ReactiveOAuth2AuthorizedClientManager authorizedClientManager(
final ReactiveClientRegistrationRepository clientRegistrationRepository,
final ReactiveOAuth2AuthorizedClientService authorizedClientService) {
logger.info("ReactiveOAuth2AuthorizedClientManager Bean Method");
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
.builder().password().build();
AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager authorizedClientManager = new AuthorizedClientServiceReactiveOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientService);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
authorizedClientManager.setContextAttributesMapper(oAuth2AuthorizeRequest -> Mono
.just(Map.of(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, System.getProperty("user"),
OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, System.getProperty("pass"))));
return authorizedClientManager;
}
/**
* The Oauth2 based WebClient bean for the web service
*
* #throws SSLException
*/
#Bean
public WebClient webClient(ReactiveOAuth2AuthorizedClientManager authorizedClientManager) throws SSLException {
String registrationId = "bael";
SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
SslProvider sslProvider = SslProvider.builder().sslContext(sslContext).build();
HttpClient httpClient = HttpClient.create().secure(sslProvider)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000).responseTimeout(Duration.ofMillis(5000))
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)));
ServerOAuth2AuthorizedClientExchangeFilterFunction oauth = new ServerOAuth2AuthorizedClientExchangeFilterFunction(
authorizedClientManager);
oauth.setDefaultClientRegistrationId(registrationId);
logger.info("WebClient Bean Method");
return WebClient.builder()
// base path of the client, this way we need to set the complete url again
.baseUrl("BASE_URL")
.clientConnector(new ReactorClientHttpConnector(httpClient))
.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).filter(logRequest())
.filter(oauth).filter(logResponse()).build();
}
So you have to make new WebClient for OAuth2 too.
In your authorizedClientManager definition add some strings(It's better to have HttpClient bean, so you won't define it all the time)
SslContext sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
SslProvider sslProvider = SslProvider.builder().sslContext(sslContext).build();
HttpClient httpClient = HttpClient.create().secure(sslProvider)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000).responseTimeout(Duration.ofMillis(5000))
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(5000, TimeUnit.MILLISECONDS))
.addHandlerLast(new WriteTimeoutHandler(5000, TimeUnit.MILLISECONDS)));
WebClient webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.build();
WebClientReactiveClientCredentialsTokenResponseClient clientCredentialsTokenResponseClient =
new WebClientReactiveClientCredentialsTokenResponseClient();
clientCredentialsTokenResponseClient.setWebClient(webClient);
and add in your authorizedClientProvider ->
ReactiveOAuth2AuthorizedClientProvider authorizedClientProvider = ReactiveOAuth2AuthorizedClientProviderBuilder
.builder().password(builder -> builder.accessTokenResponseClient(clientCredentialsTokenResponseClient)).build();

NTLM Authentication using Spring WebClient

How do we achieve NTLM authentication using Spring WebClient? All I could find is only using RestTemplate in a blocking way.
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new NTCredentials(user, password, "source-host-name", "domain-name"));
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);

Anyway to configure authorization logout url in Oauth2 client api Spring Boot

I have a logout url, to logout from the Authorization server.
Logout Url: https://.../nidp/app/logout
note: this url displays the response in jsp file
How can I configure this url in the Spring Boot Oauth2 Client application?
Currently, I have configured as below
#GetMapping(value = "/api/v1/logout", produces = MediaType.APPLICATION_JSON_VALUE)
private String checkOut()
{
RestTemplate restTemp = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
HttpEntity<String> entity = new HttpEntity<String>("", headers);
final String uri = "https://.../nidp/app/logout";
ResponseEntity<String> response = restTemp.exchange(uri, HttpMethod.GET, entity, String.class);
return "Logged out";
}

Spring Boot HTTPS with self-signed certificate

I'm making a Slack app with Spring Boot and have created an endpoint for Slack to send payloads to. Since Slack runs over HTTPS I have had to enable HTTPS by generating a self-signed certificate purely for testing purposes. I updated my application.yml with the following:
server:
port: 3000
ssl:
enabled: true
key-store: classpath:cert.p12
key-store-password: my_password
key-store-type: pkcs12
When I try to access the endpoint in my browser I get the error NET::ERR_CERT_INVALID. I then try curl on my endpoint and get the error:
curl: (60) SSL certificate problem: self signed certificate
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it.
My Slack app doesn't seem to be able to reach the endpoint either, giving an error /hello failed with the error "ssl_cacert" when I try a slash command.
Is there something else I need to do when setting up a self signed certificate? I'm aware this is unsafe in practice and therefore may be the root of these errors.
Many thanks for the help!
First of all, you have to set up your backend correctly:
Add dependency:
implementation 'org.apache.httpcomponents:httpclient:4.5'
Provide RestTemplate bean:
#Bean
private RestTemplate restTemplate() {
SSLContext sslContext = buildSslContext();
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
HttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(socketFactory)
.build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(factory);
}
private SSLContext buildSslContext() {
try {
char[] keyStorePassword = sslProperties.getKeyStorePassword();
return new SSLContextBuilder()
.loadKeyMaterial(
KeyStore.getInstance(new File(sslProperties.getKeyStore()), keyStorePassword),
keyStorePassword
).build();
} catch (Exception ex) {
throw new IllegalStateException("Unable to instantiate SSL context", ex);
} finally {
sslProperties.setKeyStorePassword(null);
sslProperties.setTrustStorePassword(null);
}
}
Provide required SSL properties in your application.properties or application.yaml file:
server:
ssl:
enabled: true
key-store: /path/to/key.keystore
key-store-password: password
key-alias: alias
trust-store: /path/to/truststore
trust-store-password: password
That's it. Now you can see your Tomcat is starting on 8080 (or another port) (https).
Alternatively, you can use my spring boot starter

Can a client make a request with RestTemplate without a certificate to a secure SSL server?

could somebody please help me with the following questions:
I have a REST server that has SSL enabled and a REST client that are on 2 different computers. Both are built with Spring boot. The server will have a .p12 or .pfx certificate.
If the REST client wants to make a request to the server, does it need to provide a certificate or can it make a request with a simple RestTemplate even if the server is secure? Does the same rules apply for Postman or can Postman send a request without a certificate as well?
I tried to create a request from the REST client, using a REST template with the certificate. But I am not sure, which certificate should I provide. Should it be the same certificate that is on the server or another one? And does the certificate from the server need to have a rule for the ip of the REST client to allow the requests?
The ssl server configuration:
ssl:
key-store-type: PKCS12
key-store: ${MY_DIR}/config/ssl/myCert.pfx
key-store-password: 123456
The rest template from the client:
RestTemplate restTemplate = null;
try {
SSLContext sslContext = SSLContextBuilder
.create()
.loadTrustMaterial(ResourceUtils.getFile("classpath:config/ssl/myCert.pfx"), password.toCharArray())
.build();
HttpClient client = HttpClients.custom()
.setSSLContext(sslContext)
.build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(client);
restTemplate = new RestTemplate(requestFactory);
} catch (KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException | KeyManagementException ex) {
LOGGER.error("Error getting the RestTemplate with ssl certificate", ex);
}
If a client certificate is required or not depends fully on the server configuration. Some servers require client certificates, some do not. Please refer to the documentation of your specific REST API to find out what the requirements on the client are.
#Bean
public RestTemplate restTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
TrustStrategy acceptingTrustStrategy = (X509Certificate[] chain, String authType) -> true;
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
SSLConnectionSocketFactory csf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(csf)
.build();
HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
return restTemplate;
}
This works for me.
please use this below import packages.
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

Resources