Упреждающая аутентификация с DefaultHttpClient
Я хочу реализовать упреждающую аутентификацию для DefaultHttpClient с NTLM. Я нашел библиотеку от Дэна Хауншелла, которая отлично работает для обычной аутентификации.
Но я не могу понять, как заставить это работать с упреждающей аутентификацией. Я нашел вопрос аутентификации Preemptive Basic с Apache HttpClient 4 с помощью этого классного ответа:
UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
HttpRequest request = ...
request.addHeader(new BasicScheme().authenticate(creds, request));
Не идеально подходит для моей проблемы, но хорошая идея. Поэтому я попытался использовать NTLMSchemeFactory
создать новый экземпляр AuthScheme
с дает мне authenticate
функция.
NTCredentials ntc = new NTCredentials("example.com/user:pwd");
httpPost.addHeader(new NTLMScheme(new JCIFSEngine()).authenticate(ntc, httpPost));
Когда эта функция будет вызвана, я получу исключение:
org.apache.http.auth.AuthenticationException: неожиданное состояние: UNINITIATED
Как я могу это исправить?
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=...
Я думаю, что первая просьба абсолютно бесполезна.
2 ответа
Мое решение заключается в следующем:
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;
}
}
С этим использованием:
NTCredentials ntc = new NTCredentials("example.com/user:password");
httpPost.addHeader(new PreemptiveNTLMHeader(httpPost, ntc));
Так будет ли DefaultHttpClient отправлять Authorization: NTLM AAA...
заголовок, который пропускает начальный запрос. После первого использования этого заголовка этот заголовок удаляет себя, чтобы избежать того, что этот фиктивный заголовок перекрывает реальный процесс аутентификации. Это работает для меня.
NTLM-аутентификация не может быть выполнена превентивно. Это сложная схема, которая включает в себя несколько (3) обменов сообщениями.