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
Related
I'm trying to connect to a website through a proxy, but I'm getting an error
Error Code: 407 Proxy Authentication Required. Forefront TMG requires authorization to fulfill the request. Access to the Web Proxy filter is denied. (12209)
My code is very close to the example that apache provides, https://hc.apache.org/httpcomponents-client-ga/examples.html (see the proxy authentication example). I'm definitely doing something wrong with authentication, but...what?
HttpHost proxy = new HttpHost("http-proxy", 80);
HttpHost target = new HttpHost(url, 80);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(user,password));
try (CloseableHttpClient client = HttpClientBuilder.create().setDefaultCredentialsProvider(new SystemDefaultCredentialsProvider()).build()) {
RequestConfig config = RequestConfig.custom().setProxy(proxy).build();
HttpGet httpget = new HttpGet("/basic-auth/user/passwd");
httpget.setConfig(config);
HttpResponse response = client.execute(target, httpget);
}
The problem seems to be that you are setting new SystemDefaultCredentialsProvider() when you are building the HTTP client. I guess you intention was to set credsProvider, to which you have just added proxy user and password.
I have heard that Post is most preferred while reaching a link which has password /sensitive information.
Q1.I know that Post is better than Put for Name-value pair parameters as ,the Get exposes it in URL and Post doesn't. But if we are talking about authentication ,How does it matter what method i use as i set the Credentials to the HttpClient not to the HttpGet or HttpPost.So credentials are not getting exposed anyway .The encapsulation of Name-value pairs is benefited by using POST.
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(target.getHostName(), target.getPort()),
new UsernamePasswordCredentials("user", "passwd"));
CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(credsProvider)
.build();
Q2. Again i can set the credentials as header to httppost as well to httpget like this..
String encoding = new BASE64Encoder().encode("user:passwd".getBytes());
httpGet.setHeader("Authorization", "Basic " + encoding);
httpPost.setHeader("Authorization", "Basic " + encoding);
Where does the post method takes precedence over get method?
I think whoever said post is better than get when using passwords meant that the passwords would be part of the url in the get request, which would obviously be bad.
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);
I'm running HttpClient 4.3.6 in Java 6. When I run the following code, the authentication appears to succeed. The Status Code returned is 200. However, I'm getting the following error message in the console:
WARNING: NEGOTIATE authentication error: Invalid name provided (Mechanism level: Could not load configuration file C:\Windows\krb5.ini (the system cannot find the file specified))
How do I eliminate this warning?
CloseableHttpClient httpclient = HttpClients.createDefault();
HttpContext localContext = new BasicHttpContext();
HttpGet method = new HttpGet(url);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope(host, 80),
new NTCredentials(userid, password, host, login_domain));
localContext.setAttribute(HttpClientContext.CREDS_PROVIDER, credsProvider);
String filePath = null;
// Execute the method.
CloseableHttpResponse clientResponse = httpclient.execute(method, localContext);
HttpEntity entity = clientResponse.getEntity();
int statusCode = clientResponse.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
System.err.println("Method failed: " + method.getRequestLine());
}
You need to pass in a set of target preferred auth schemes:
Create your httpClient like this:
PoolingHttpClientConnectionManager connPool = new PoolingHttpClientConnectionManager();
connPool.setMaxTotal(200);
connPool.setDefaultMaxPerRoute(200);
// Authentication
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY, new NTCredentials(username, password, workstation, domain));
RequestConfig config = RequestConfig.custom().setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM)).build();
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(connPool).setDefaultRequestConfig(config).build();
HttpClientContext context = HttpClientContext.create();
context.setCredentialsProvider(credsProvider);
Yes I believe that, in fact, your authentication is successful and is probably just falling back to NTLM from Kerberos. My code looks similar to yours and in my application I'm connecting to SharePoint using HttpClient 4.3.5 in Java 7. When SharePoint is configured to "Negotiate" (Attempt Kerberos and then failover to NTLM), I will see a similar error to what you reported in the HttpClient generated logging, specifically:
Selected authentication options: [NEGOTIATE, NTLM]
Executing request GET /my/personal/user2/_api/web?$select=ServerRelativeUrl HTTP/1.1
Target auth state: CHALLENGED
Generating response to an authentication challenge using Negotiate scheme
init XXX.XXX.XXX.XXX:80
NEGOTIATE authentication error: org.ietf.jgss.GSSException, major code: 11, minor code: 0
major string: General failure, unspecified at GSSAPI level
minor string: Desired initLifetime zero or less
Generating response to an authentication challenge using ntlm scheme
Following that, it will successfully authenticate via NTLM. So, I read that error message as saying "Kerberos didn't work, now we'll use NTLM". As long as you're getting a 200 response, you should be good to go.
Are you sure authentication is happening successfully, if the website is set to Negotiate (Attempt Kerbero, then failover to NTLM) BASIC authentication would probably not be successful.
I'm trying to use the Apache/Jakarta HttpClient 4.1.1 to connect to an arbitrary web page using the given credentials. To test this, I have a minimal install of IIS 7.5 on my dev machine running where only one authentication mode is active at a time. Basic authentication works fine, but Digest and NTLM return 401 error messages whenever I try to log in. Here is my code:
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost/");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new NTCredentials("user", "password", "", "localhost"));
if (!new File(System.getenv("windir") + "\\krb5.ini").exists()) {
List<String> authtypes = new ArrayList<String>();
authtypes.add(AuthPolicy.NTLM);
authtypes.add(AuthPolicy.DIGEST);
authtypes.add(AuthPolicy.BASIC);
httpclient.getParams().setParameter(AuthPNames.PROXY_AUTH_PREF,
authtypes);
httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF,
authtypes);
}
localContext.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
HttpResponse response = httpclient.execute(httpget, localContext);
System.out.println("Response code: " + response.getStatusLine());
The one thing I've noticed in Fiddler is that the hashes sent by Firefox versus by HttpClient are different, making me think that maybe IIS 7.5 is expecting stronger hashing than HttpClient provides? Any ideas? It'd be great if I could verify that this would work with NTLM. Digest would be nice too, but I can live without that if necessary.
I am not an expert on the subject but during the NTLM authentication using http components I have seen that the client needs 3 attempts in order to connect to an NTML endpoint in my case. It is kinda described here for Spnego but it is a bit different for the NTLM authentication.
For NTLM in the first attempt client will make a request with Target auth state: UNCHALLENGED and Web server returns HTTP 401 status and a header: WWW-Authenticate: NTLM
Client will check for the configured Authentication schemes, NTLM should be configured in client code.
Second attempt, client will make a request with Target auth state: CHALLENGED, and will send an authorization header with a token encoded in base64 format: Authorization: NTLM TlRMTVNTUAABAAAAAYIIogAAAAAoAAAAAAAAACgAAAAFASgKAAAADw==
Server again returns HTTP 401 status but the header: WWW-Authenticate: NTLM now is populated with encoded information.
3rd Attempt Client will use the information from WWW-Authenticate: NTLM header and will make the final request with Target auth state: HANDSHAKE and an authorisation header Authorization: NTLM which contains more information for the server.
In my case I receive an HTTP/1.1 200 OK after that.
In order to avoid all this in every request documentation at chapter 4.7.1 states that the same execution token must be used for logically related requests. For me it did not worked.
My code:
I initialize the client once in a #PostConstruct method of an EJB
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(18);
cm.setDefaultMaxPerRoute(6);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(30000)
.setConnectTimeout(30000)
.setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM))
.setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC))
.build();
CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new NTCredentials(userName, password, hostName, domainName));
// Finally we instantiate the client. Client is a thread safe object and can be used by several threads at the same time.
// Client can be used for several request. The life span of the client must be equal to the life span of this EJB.
this.httpclient = HttpClients.custom()
.setConnectionManager(cm)
.setDefaultCredentialsProvider(credentialsProvider)
.setDefaultRequestConfig(requestConfig)
.build();
Use the same client instance in every request:
HttpPost httppost = new HttpPost(endPoint.trim());
// HttpClientContext is not thread safe, one per request must be created.
HttpClientContext context = HttpClientContext.create();
response = this.httpclient.execute(httppost, context);
Deallocate the resources and return the connection back to connection manager, at the #PreDestroy method of my EJB:
this.httpclient.close();
I had the same problem with HttpClient4.1.X After upgrading it to
HttpClient 4.2.6 it woked like charm. Below is my code
DefaultHttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("url");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(AuthScope.ANY,
new NTCredentials("username", "pwd", "", "domain"));
List<String> authtypes = new ArrayList<String>();
authtypes.add(AuthPolicy.NTLM);
httpclient.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF,authtypes);
localContext.setAttribute(ClientContext.CREDS_PROVIDER, credsProvider);
HttpResponse response = httpclient.execute(httpget, localContext);
HttpEntity entity=response.getEntity();
The easiest way troubleshoot such situations I found is Wireshark. It is a very big hammer, but it really will show you everything. Install it, make sure your server is on another machine (does not work with Localhost) and start logging.
Run your request that fails, run one that works. Then, filter by http (just put http in the filter field), find the first GET request, find the other GET request and compare. Identify meaningful difference, you now have specific keywords or issues to search code/net for. If not enough, narrow down to first TCP conversation and look at full request/response. Same with the other one.
I solved an unbelievable number of problems with that approach. And Wireshark is very useful tool to know. Lots of super-advanced functions to make your network debugging easier.
You can also run it on either client or server end. Whatever will show you both requests to allow you to compare.
I had a similar problem with HttpClient 4.1.2. For me, it was resolved by reverting to HttpClient 4.0.3. I could never get NTLM working with 4.1.2 using either the built-in implementation or using JCIFS.
Updating our application to use the jars in the httpcomponents-client-4.5.1 resolved this issue for me.
I finally figured it out. Digest authentication requires that if you use a full URL in the request, the proxy also needs to use the full URL. I did not leave the proxy code in the sample, but it was directed to "localhost", which caused it to fail. Changing this to 127.0.0.1 made it work.