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);
Related
How to call https endpoint with httpclient5. I cant use this way
SSLConnectionSocketFactory socketFactory =
new SSLConnectionSocketFactory(sslContext);
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
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();
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";
}
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;
I am invoking a multipart request using Resttemplate, in one of our environment, we are getting an exception:
Invalid mime type "{application/json, q=1000}": Invalid token character '{' in token "{
On some debugging, we found it is due to Jackson parsing, we followed some of these recommendations,
Added content negotiation headers:
baseHeader.add(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE);
baseHeader.add(HttpHeaders.ACCEPT_CHARSET, StandardCharsets.UTF_8.name()
);
And modified Resttemplate Configs:
#Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(requestFactory);
List<HttpMessageConverter<?>> converters = new ArrayList<>();
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setWriteAcceptCharset(false);
converters.add(stringConverter);
restTemplate.setMessageConverters(converters);
restTemplate.getMessageConverters().add(new ByteArrayHttpMessageConverter());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
return new RestTemplate(requestFactory);
}
But both didn't worked, we updated Spring version to 2.1.6, still, the issue exists.