Add proxy information and basic auth to the resttemplate using httpClient - spring

My development environment is behind a proxy so i need to set the proxy information to the rest template, that's all good when i use a HttpComponentsClientHttpRequestFactory and set the proxy setting in the httpClient and set it in the template.
But now i have a rest service that needs basic auth. And to set the basic auth credentials, i need to set them in the httpClient on the rest template. But i see that the getparams method in the httpClient is depricated, so i can't just update the existing client in the template, and if i create a new httpclient object, i will overwrite the proxy info that were set during the application bootstrapping.
So is there some way that i could extract the httpClient from the rest template and update it? Or is there any other way to tackle this?
Thanks.

Configure the httpClient as follows:
HttpHost target = new HttpHost("hostname", 80, "http");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("user", "passwd"));
HttpHost proxy = new HttpHost("proxy", 12345);
CloseableHttpClient httpclient = HttpClients.custom()
.setProxy(proxy)
.setDefaultCredentialsProvider(credsProvider).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpclient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
See also HttpClient Examples

The above solution did not work for me i work around the above and finally make it work with small modifications.
RestTemplate restTemplate = new RestTemplate();
HttpHost proxy =null;
RequestConfig config=null;
String credentials = this.env.getProperty("uname") + ":" + this.env.getProperty("pwd");
String encodedAuthorization = Base64.getEncoder().encodeToString(credentials.getBytes());
Header header = new BasicHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodedAuthorization);
List<Header> headers = new ArrayList<>();
headers.add(header);
if(Boolean.valueOf(env.getProperty("proxyFlag"))){
proxy = new HttpHost(this.env.getProperty("proxyHost"), Integer.parseInt(env.getProperty("proxyPort")), "http");
config= RequestConfig.custom().setProxy(proxy).build();
}else{
config= RequestConfig.custom().build();
}
CloseableHttpClient httpClient = HttpClientBuilder.create().setDefaultRequestConfig(config)
.setDefaultHeaders(headers).build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
restTemplate.setRequestFactory(factory);
return restTemplate;

Related

Rest Template release Connection from Pool

I have rest template config similar to the following. I am trying to release a connection from the pool if I get a status code that does not equal 2XX (long story but need this code). Is here a way I can get the connection Manager and release a specific connection?
#Bean
public RestTemplate restTemplate() {
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(100);
connectionManager.setDefaultMaxPerRoute(20);
RequestConfig requestConfig = RequestConfig
.custom()
.setConnectionRequestTimeout(5000) // timeout to get connection from pool
.setSocketTimeout(5000) // standard connection timeout
.setConnectTimeout(5000) // standard connection timeout
.build();
HttpClient httpClient = HttpClientBuilder.create()
.setConnectionManager(connectionManager)
.setDefaultRequestConfig(requestConfig).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
return new RestTemplate(requestFactory);
}
Looking for a way to accomplish something similar to the following
if(!httpStatusCode.substr(1).equals("2")) {
restTemplate.getConnectionPool().relase().thisConnection();
}
enter code here

HttpClient have both SSL and Proxy authentication configured?

I have two pieces of code using HttpClient,
First part in case that the end point requires SSL
Second is proxy connection with basic authentication
My question Is how can I make this code conditional so in cases i have SSL + Proxy or SSL only
I have hard time figuring out how to set the default credentials for example after I created the client using the client in the SSL part
.setDefaultCredentialsProvider(credsProvider)
This part is how I create the Client when I need SSL
CloseableHttpClient client = null;
if(conf.isUseSslConfig()) {
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(conf.getTrustStoreLocation()), conf.getTrustStorePassword().toCharArray(), new TrustSelfSignedStrategy()).build();
// Allow protocols
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,conf.getTlsVersions(), null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
}else {
client= HttpClients.createDefault();
}
And this part is how I create the Client when I need Proxy authentication:
if(conf.isUseProxyConfig()){
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("fakeProxy.xerox.com", 80),
new UsernamePasswordCredentials("xeroxUser","fakePassword123"));
HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider).build();
}
So the bottom line is how to make the two sections work together so in case
Call with SSL + Proxy and authentication
Call with only SSL
Call with only Proxy and authentication
You can write code this way to get multiple conditions resolved :
CloseableHttpClient client = null;
if(conf.isUseSslConfig() && conf.isUseProxyConfig()) {
setSSLSetting(client);
setProxy()
}else if(conf.isUseSslConfig()) {
setSSLSetting(client);
}else {
client= HttpClients.createDefault();
}
private void setProxy(){
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(new AuthScope("fakeProxy.xerox.com", 80),new UsernamePasswordCredentials("xeroxUser","fakePassword123"));
}
private void setSSLSetting(CloseableHttpClient client){
SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(new File(conf.getTrustStoreLocation()), conf.getTrustStorePassword().toCharArray(), new TrustSelfSignedStrategy()).build();
// Allow protocols
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,conf.getTlsVersions(), null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
client = HttpClients.custom().setSSLSocketFactory(sslsf).build();
}
or you can create methods that return client with different settings and configs like this :
final Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", new PlainConnectionSocketFactory()).register("https", sslsf).build();
final PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
private CloseableHttpClient createHttpClient(String headerName, String value) throws NoSuchAlgorithmException, KeyManagementException,KeyStoreException {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
Header header = new BasicHeader(headerName,value);
List<Header> headers = new ArrayList<>();
headers.add(header);
RequestConfig reqConfig = RequestConfig.custom().setConnectionRequestTimeout(long milli seconds).build();
CloseableHttpClient httpclient = HttpClients.custom().
setDefaultHeaders(headers).
setDefaultRequestConfig(reqConfig).
setConnectionManager(cm).
build();
return httpclient;
}

How to set cipher suites with javax.net.ssl.SSLContext

In a spring boot application using java8, I am setting the underlying SSLConext of an httpClient connection as follows:
import javax.net.ssl.SSLContext;
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
sslContext.init(null, null, null);
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.setConnectionManager(myConnectionManager)
.setDefaultRequestConfig(rqConfig)
.setSSLContext(sslContext)
.build();
I need to set the cipher suites for the underlying TLS1.2 secured connection to something stronger of my choice. I don't see a way to do this with the way I am creation the sslContext in my code.
Can someone help me set up the cipher suites with my sslContext ?
================UPDATE=================
This is how I have now created my HttpClient
CloseableHttpClient httpClient = HttpClientBuilder
.create()
.setConnectionManager(myConnectionManager)
.setDefaultRequestConfig(rqConfig)
.setSSLSocketFactory(new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[] {"some-gibberish-cipher-suite"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build();
Preferred TLS protocol versions and custom ciphers can be specified when creating a custom SSLConnectionSocketFactory instance
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[] {"TLS_RSA_WITH_AES_256_CBC_SHA256"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build();
try (CloseableHttpResponse response = client.execute(new HttpGet("https://httpbin.org/"))) {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
}
Alternatively, one can create a custom PoolingHttpClientConnectionManager instance with the desired SSL configuration.
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", new SSLConnectionSocketFactory(
SSLContexts.createSystemDefault(),
new String[]{"TLSv1.2"},
new String[]{"TLS_RSA_WITH_AES_256_CBC_SHA256"},
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
.build());
CloseableHttpClient client = HttpClients.custom()
.setConnectionManager(cm)
.build();
try (CloseableHttpResponse response = client.execute(new HttpGet("https://httpbin.org/"))) {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
}

OAuth2.0 authentication of Dynamics CRM WebAPIs from a background Java (Spring) application

I need to authenticate against OAuth2.0 Microsoft Dynamics CRM from a background Java application; background because it's an integration app between the ERP of the customer and its Dynamics online instance.
I tried to use spring-security-oauth2 classes to get an high level set of resource to handle authentication, but i can't retrieve the initial token, while I'm successful if I try with building "manually" the http requests needed.
I wrote a simple Java application to test the authentication and I had this piece of code working, with content that is the String representation of the access token JSon:
String accessTokenURL = "https://login.microsoftonline.com/common/oauth2/token";
CloseableHttpClient client = HttpClients.createDefault();
HttpPost requestToken = new HttpPost(accessTokenURL);
requestToken.addHeader("Cache-Control", "no-cache");
requestToken.addHeader("Content-Type", "application/x-www-form-urlencoded");
List<NameValuePair> params = new ArrayList<NameValuePair>();
params.add(new BasicNameValuePair("grant_type", "password"));
params.add(new BasicNameValuePair("client_id", clientId));
params.add(new BasicNameValuePair("resource", resource));
params.add(new BasicNameValuePair("username", username));
params.add(new BasicNameValuePair("password", password));
params.add(new BasicNameValuePair("client_secret", clientSecret));
requestToken.setEntity(new UrlEncodedFormEntity(params));
CloseableHttpResponse response = client.execute(requestToken);
InputStream is = response.getEntity().getContent();
String content = IOUtils.toString(is);
System.out.println(content);
client.close();
resource is the Dynamics online instance of the customer.
I tried something similar using Spring Security OAuth2 client classes but I always get "401 Unauthorized":
ResourceOwnerPasswordResourceDetails resourceObj = new ResourceOwnerPasswordResourceDetails();
resourceObj.setClientId(clientId);
resourceObj.setClientSecret(clientSecret);
resourceObj.setGrantType("password");
resourceObj.setAccessTokenUri(accessTokenURLWithResource);
// resourceObj.setId(resource);
resourceObj.setTokenName("bearer_token");
resourceObj.setUsername(username);
resourceObj.setPassword(password);
AccessTokenRequest atr = new DefaultAccessTokenRequest();
Map<String, List<String>> headersMap = new HashMap<String, List<String>>();
headersMap.put("Cache-Control", Arrays.asList("no-cache"));
headersMap.put("Content-Type", Arrays.asList("application/x-www-form-urlencoded"));
atr.add("client_id", clientId);
atr.add("resource", resource);
atr.add("client_secret", clientSecret);
atr.add("username", username);
atr.add("password", password);
OAuth2ClientContext context = new DefaultOAuth2ClientContext(atr);
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceObj, context);
OAuth2AccessToken token = restTemplate.getAccessToken();
System.out.println(new Gson().toJson(token));
I tried using different ways to pass the access token URL and resource but the result is always the same.
Any help or any other advice about other high level library to be used in this case are appreciated, thanks.
Thanks to some hints of #fateddy I came to a solution. This piece of code works, now I'll try to integrate in my application
ResourceOwnerPasswordResourceDetails resourceObj = new ResourceOwnerPasswordResourceDetails();
// resourceObj.setClientId(clientId);
// resourceObj.setClientSecret(clientSecret);
resourceObj.setGrantType("password");
resourceObj.setAccessTokenUri(accessTokenURLWithResource);
// resourceObj.setId(resource);
resourceObj.setTokenName("bearer_token");
// resourceObj.setUsername(username);
// resourceObj.setPassword(password);
AccessTokenRequest atr = new DefaultAccessTokenRequest();
Map<String, List<String>> headersMap = new HashMap<String, List<String>>();
headersMap.put("Cache-Control", Arrays.asList("no-cache"));
headersMap.put("Content-Type", Arrays.asList("application/x-www-form-urlencoded"));
atr.add("client_id", clientId);
atr.add("resource", resource);
atr.add("client_secret", clientSecret);
atr.add("username", username);
atr.add("password", password);
OAuth2ClientContext context = new DefaultOAuth2ClientContext(atr);
OAuth2RestTemplate restTemplate = new OAuth2RestTemplate(resourceObj, context);
OAuth2AccessToken token = restTemplate.getAccessToken();
System.out.println(new Gson().toJson(token));

Spring REST template - 401 Unauthorized error

I am using Spring Rest Template inside a Spring Boot Application.
I always get 401 Unauthorized error even though I am passing the credentials.
I am able to access this service by Chrome REST Web Service Client.
Is there a simplified way to access the REST template in SpringBoot.
Below is the code snippet done so far which results in 401 error
private DetailsBean invokeDetailsRestService(UserParam userParam){
ResponseEntity<DetailsBean> responseEntity = null;
String url = "https://dev.com/app/identifyuser/";
RestClientConfig restClientConfig =new RestClientConfig("user123","pass123");
responseEntity= restClientConfig.postForEntity(url, userParam, DetailsBean.class);
log.debug("User Details : {} ", responseEntity.getBody());
return responseEntity.getBody();
}
public ClientHttpRequestFactory getRequestFactory(String userName,String password){
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials( new AuthScope(null, -1), new UsernamePasswordCredentials(userName,password) );
HttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
RestClientConfig class
public RestClientConfig(String username, String password) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(null, -1),
new UsernamePasswordCredentials(username, password));
HttpClient httpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).build();
setRequestFactory(new HttpComponentsClientHttpRequestFactory(httpClient));
}
Error:
WARN c7af55b5-1cac-4db6-a202-202416c27ba4
12612 --- [apr-8082-exec-8] o.a.http.impl.auth.HttpAuthenticator
: NEGOTIATE authentication error:
No valid credentials provided (Mechanism level:
No valid credentials provided (Mechanism level:
Failed to find any Kerberos tgt))
The authorization issue was fixed with the below code..
Credentials should be passed to a Spring REST Template with the below code:
String userAndPass = "Test:Test123";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
headers.add("Authorization", "Basic " + Base64Utility.encode(userAndPass.getBytes()));
I faced similar issue when i'm trying to make call to webservice, this solved my issue:
restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor("userName", "password"));
restTemplate.postForObject('','',''');
Pass the credentials like this, it should solve the issue.
I used spring boot 2.2.4.RELEASE version. then I work below way.
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(username, password));
RequestDto requestDto = new RequestDto();
// set parameter
ResponseDto response = restTemplate.postForObject(URL, requestDto, ResponseDto.class);
Or
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(username, password);
RequestDto requestDto = new RequestDto();
// set parameter
HttpEntity<RequestDto> request = new HttpEntity<>(requestDto, headers);
ResponseDto response = restTemplate.postForObject(URL, request, ResponseDto.class);

Resources