I want to implement a preemptive authentication for the DefaultHttpClient with NTLM. I found a libary from Dan Hounshell which works fine for normal authentication.
But I cannot figger out how to make this work with preemptive authentication. I found the question Preemptive Basic authentication with Apache HttpClient 4 with this cool answer:
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
HttpRequest request = ...
request.addHeader(new BasicScheme().authenticate(creds, request));
Not perfect for my problem but a good idea. So I tried to use the NTLMSchemeFactory
to create a new instance of AuthScheme
with gives me the authenticate
function.
NTCredentials ntc = new NTCredentials("example.com/user:pwd");
httpPost.addHeader(new NTLMScheme(new JCIFSEngine()).authenticate(ntc, httpPost));
When this function is called I'll get an exception:
org.apache.http.auth.AuthenticationException: Unexpected state: UNINITIATED
How can I fix that?
POST /login/ HTTP/1.1
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
Host: example.com
Connection: Keep-Alive
data=...
HTTP/1.1 401 Unauthorized
Content-Type: text/html
Server: Microsoft-IIS/7.5
WWW-Authenticate: Negotiate
WWW-Authenticate: NTLM
X-Powered-By: ASP.NET
Date: Wed, 12 Dec 2012 14:36:26 GMT
Content-Length: 1344
Much data...
POST /login/ HTTP/1.1
Content-Length: 21
Content-Type: application/x-www-form-urlencoded
Host: example.com
Connection: Keep-Alive
Authorization: NTLM AAABBBCCC...FFF==
data=...
I think the first request is absolut useless.
My solution is this here:
public class PreemptiveNTLMHeader implements Header {
private HttpRequest request;
private NTCredentials ntc;
public PreemptiveNTLMHeader(HttpRequest request, NTCredentials ntc) {
this.request = request;
this.ntc = ntc;
}
/* (non-Javadoc)
* @see org.apache.http.Header#getName()
*/
public String getName() {
return "Authorization";
}
/* (non-Javadoc)
* @see org.apache.http.Header#getValue()
*/
public String getValue() {
request.removeHeader(this);
try {
return "NTLM " + new JCIFSEngine().generateType1Msg(ntc.getDomain(), ntc.getWorkstation());
} catch(NTLMEngineException e) {
return "Failed";
}
}
/* (non-Javadoc)
* @see org.apache.http.Header#getElements()
*/
public HeaderElement[] getElements() throws ParseException {
return null;
}
}
With this usage:
NTCredentials ntc = new NTCredentials("example.com/user:password");
httpPost.addHeader(new PreemptiveNTLMHeader(httpPost, ntc));
So will the DefaultHttpClient send a Authorization: NTLM AAA...
header which skips the initial request. After the first usage of this header this header deletes itself to avoid that this dummy header overrides the real auth process. This works for me.