HttpComponentMessageSender NTLM - java

Today I was trying to configure the HttpComponentsMessageSender which uses Apache's org.apache.httpcomponents.httpclient library. Before I used the standard Java classes (I think java.net.HttpUrlMessageSender or something along those lines).
My HTTP methods need to authenticate using NTLM and the software is running under Linux so I cannot use the default Windows mechanism to do authentication, but instead I must configure authentication inside the Java application.
Originally I was just using a custom implementation of java.net.Authenticator which I used to set the username and password
for the NTLM access. But when I switched to HttpComponentsMessageSender this approach did not work anymore. When I tried to setup a custom HttpClient configuration with the HttpComponentsMessageSender I ran into various issues which I thought I would document here.
I am going to post my own answer but if anyone has a better solution, please feel free to add your solution.

I solved this adding a custom build HttpClient object which I built with the HttpClientBuilder. However adding this caused the following exception to appear:
org.apache.http.ProtocolException: Content-Length header already present
The solution which I found in this answer was to add a HttpRequestInterceptor.
Using the setConnectionTimeout on the HttpComponentsMessageSender did not work anymore with my custom HttpClient object. Instead I had to inject a RequestConfig object into the HttpClientBuilder.
The NTCredentials object required the domain name as a parameters. Earlier when I was using the Authenticator interface from java.net it was enough to just supply the username and password.
This is the code I am using currently:
HttpComponentsMessageSender messageSender = new HttpComponentsMessageSender ();
AuthScope authscope;
NTCredentials credentials;
CredentialsProvider credentialsProvider;
Registry<AuthSchemeProvider> registry;
RequestConfig requestConfig;
authscope = new AuthScope (HOST_IP, HOST_PORT);
credentials = new NTCredentials ("user", "pass", null, "domain");
credentialsProvider = new BasicCredentialsProvider ();
credentialsProvider.setCredentials (authscope, credentials);
registry = RegistryBuilder.<AuthSchemeProvider>create ()
.register(AuthSchemes.NTLM, new NTLMSchemeFactory ())
.build ();
HttpRequestInterceptor interceptor
= (request, context) -> request.removeHeaders(HTTP.CONTENT_LEN);
requestConfig = RequestConfig.custom ()
.setConnectTimeout (3000)
.build ();
HttpClient httpClient
= HttpClientBuilder.create ()
.setDefaultRequestConfig (requestConfig)
.setDefaultAuthSchemeRegistry (registry)
.setDefaultCredentialsProvider (credentialsProvider)
.addInterceptorFirst (interceptor)
.build ();
messageSender.setHttpClient (httpClient);

Related

HttpTransportProperties.Authenticator is not found in Axis2

Just generated Java code from WSDL using Apache Axis 2. The service is protected with basic authentication. When I try to create authentication object in order to set the username and password, the class (HttpTransportProperties.Authenticator) is not found in the library.
How can I set basic authentication for the client code generated by Apache Axis2?
Here is the old way of setting basic authentication params:
HttpTransportProperties.Authenticator basicAuth = new HttpTransportProperties.Authenticator();
basicAuth.setUsername("username");
basicAuth.setPassword("password");
basicAuth.setPreemptiveAuthentication(true);
final Options clientOptions = stub._getServiceClient().getOptions();
clientOptions.setProperty(HTTPConstants.AUTHENTICATE, basicAuth);
stub._getServiceClient().setOptions(clientOptions);
I had the same Problem!
Solution: Use HttpTransportPropertiesImpl instead of HttpTransportProperties.
org.apache.axis2.client.OperationClient _operationClient = _serviceClient.createClient(_operations[1].getName());
_operationClient.getOptions()
.setAction("http://asdf/checkOutRequest");
HttpTransportPropertiesImpl.Authenticator basicAuth = new HttpTransportPropertiesImpl.Authenticator();
basicAuth.setUsername("tomcat");
basicAuth.setPassword("tomcat");
basicAuth.setPreemptiveAuthentication(true);
final Options clientOptions = _operationClient.getOptions();
clientOptions.setProperty(HTTPConstants.AUTHENTICATE, basicAuth);
_operationClient.setOptions(clientOptions);
For anybody needing a even more explicit example of uwesch's very helpful answer.

Do Jersey client support NTLM proxy

I'm trying to make a jersey client call using NTLM proxy? is that possible as i was not able to get any clear information on the same. Did anyone tried before?
Yes it is possible to configure the Jersey Client to connect through a proxy server that requires NTLM authentication.
Here is a simplified code snippet that prepares a suitable ClientConfig that should work with Jersey v2.5+:
final ClientConfig config = new ClientConfig();
config.property(ClientProperties.PROXY_URI, "http://myproxy.com:8000");
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
final AuthScope ntlmAuthScope =
new AuthScope("myproxy.com", 8000, AuthScope.ANY_REALM, "NTLM");
credentialsProvider.setCredentials(
ntlmAuthScope,
new NTCredentials("user", "password", "hostname", "domain") );
config.property(
ApacheClientProperties.CREDENTIALS_PROVIDER, credentialsProvider);
config.connectorProvider(new ApacheConnectorProvider());
Client client = ClientBuilder.newClient(config);
Please note: I am using the Apache HttpClient connector with Jersey Client - you may require slightly different code if you are using another client transport connector.
You may also need to add the following line to your code if you want your POST/PUT requests to be buffered (and therefore repeatable) in response to any 407 authentication challenges that come back from your proxy server:
config.property(ClientProperties.REQUEST_ENTITY_PROCESSING,
RequestEntityProcessing.BUFFERED);

Is there any way with apache httpclient to set credentials based of a URI

Currently I have some uris that I call using resteasy that That looks something like this:
http://host.com/api/project1/getsomestuff
and
http://host.com/api/project2/getsomestuff
Both use digest authentication but require a different username and password for authentication. Currently in code I have to handle this by creating a different client instance for each project like this:
DefaultHttpClient project1Client = new DefaultHttpClient();
Credentials project1Credentials = new UsernamePasswordCredentials("user1", "password1");
project1Client.getCredentialsProvider().setCredentials(AuthScope.ANY, project1Credentials);
ClientExecutor executor1 = new ApacheHttpClient4Executor(project1Client);
MyService project1Proxy = ProxyFactory.create(MyService.class, executor1);
project1Proxy.getSomeStuff("project1");
DefaultHttpClient project2Client = new DefaultHttpClient();
Credentials project2Credentials = new UsernamePasswordCredentials("user2", "password2");
project2Client.getCredentialsProvider().setCredentials(AuthScope.ANY, project1Credentials);
ClientExecutor executor2 = new ApacheHttpClient4Executor(project1Client);
MyService project2Proxy = ProxyFactory.create(MyService.class, executor2);
project2Proxy.getSomeStuff("project2");
I have looked through AuthScope and CredentialsProvider and I just can't see any way how this would be possible. Does anybody know of any way to use both sets of credentials using the same client or am I stuck using multiple clients?
Also, please note that I do not control the server side of this rest call and both calls use the same realm so I am unable to set the different credentials per realm.
If those applications have a different security contexts they are also likely to have different authentication realms (or at least they are expected to). One can explicitly set the realm attribute on the AuthScope object to explicitly match it to a specific authentication context with the given realm name.
I think you are out of luck. Looking through the docs you cannot create an Authscope which sends a different password to different paths on the same host and port.

HttpClient 4.2, Basic Authentication, and AuthScope

I have an application connecting to sites that require basic authentication. The sites are provided at run time and not known at compile time.
I am using HttpClient 4.2.
I am not sure if the code below is how I am supposed to specify basic authentication, but the documentation would suggest it is. However, I don't know what to pass in the constructor of AuthScope. I had thought that a null parameter meant that the credentials supplied should be used for all URLs, but it throws a NullPointerException, so clearly I am wrong.
m_client = new DefaultHttpClient();
UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(m_userName, m_password);
((DefaultHttpClient)m_client).getCredentialsProvider().setCredentials(new AuthScope((HttpHost)null), credentials);
AuthScope.ANY is what you're after: http://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/auth/AuthScope.html
Try this:
final HttpClient client = new HttpClient();
client.getParams().setAuthenticationPreemptive(true);
client.getState().setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user.getUsername(), user.getPassword()));
final GetMethod method = new GetMethod(uri);
client.executeMethod(method);
From at least version 4.2.3 (I guess after version 3.X), the accepted answer is no longer valid. Instead, do something like:
private HttpClient createClient() {
HttpParams params = new BasicHttpParams();
HttpProtocolParams.setContentCharset(params, "UTF-8");
Credentials credentials = new UsernamePasswordCredentials("user", "password");
DefaultHttpClient httpclient = new DefaultHttpClient(params);
httpclient.getCredentialsProvider().setCredentials(AuthScope.ANY, credentials);
return httpclient;
}
The JavaDoc for AuthScope.ANY says In the future versions of HttpClient the use of this parameter will be discontinued, so use it at your own risk. A better option would be to use one of the constructors defined in AuthScope.
For a discussion on how to make requests preemptive, see:
Preemptive Basic authentication with Apache HttpClient 4

Proper use of HttpRequestInterceptor and CredentialsProvider in doing preemptive authentication with HttpClient

I'm writing an application in Android that consumes some REST services I've created. These web services aren't issuing a standard Apache Basic challenge / response. Instead in the server-side code I'm wanting to interrogate the username and password from the HTTP(S) request and compare it against a database user to make sure they can run that service.
I'm using HttpClient to do this and I have the credentials stored on the client after the initial login (at least that's how I see this working). So here is where I'm stuck. Preemptive authenticate under HttpClient requires you to setup an interceptor as a static member. This is the example Apache Components uses.
HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {
#Override
public void process( final HttpRequest request, final HttpContext context) throws HttpException, IOException {
AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE);
CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute(
ClientContext.CREDS_PROVIDER);
HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST);
if (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort());
Credentials creds = credsProvider.getCredentials(authScope);
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
};
So the question would be this. What would the proper use of this be? Would I spin this up as part of the application when the application starts? Pulling the username and password out of memory and then using them to create this CredentialsProvider which is then utilized by the HttpRequestInterceptor? Or is there a way to do this more dynamically?
HttpClient does not like pre-emptive authentication very much.
If your REST API supports BASIC authentication, then it is probably simpler to just put in the proper header yourself. Here is a sample Twitter client, using the Twitter API, that uses this technique.
Nevermind, I found out what was wrong. I took another look at the Apache examples and realized that I wasn't passing along the "http" when creating the HttpHost object I was using. It was completely unrelated. Ugh.

Categories