通過抓包分析,局域網使用的是NTLM 協議。
當通過瀏覽器訪問被NTLM協議保護的資源的時候,NTLM的認證方式和流程如下:
1: C --> S GET ... 2: C <-- S 401 Unauthorized WWW-Authenticate: NTLM 3: C --> S GET ... Authorization: NTLM <base64-encoded type-1-message> 4: C <-- S 401 Unauthorized WWW-Authenticate: NTLM <base64-encoded type-2-message> 5: C --> S GET ... Authorization: NTLM <base64-encoded type-3-message> 6: C <-- S 200 Ok
Type-1消息包括機器名、Domain等
Type-2消息包括server發出的NTLM challenge
Type-3消息包括用戶名、機器名、Domain、以及兩個根據server發出的challenge計算出的response,這里response是基于challenge和當前用戶的登錄密碼計算而得
PS:在第二步時,當瀏覽器接收到一個401 Unauthorized 的response,會彈出該對話框讓用戶輸入用戶名、密碼。(ie有可能會自動登錄)我的程序(client)要和另個程序走http接口通訊(server),server再去ad驗證域登錄
httpclient 實現NTLM驗證(當然你也可以自己實現協議)
HttpClient從version 4.1 開始完全支持NTLM authentication protocol(NTLMv1, NTLMv2, and NTLM2 ),文檔的原話是“The NTLM authentication scheme is significantly more expensive in terms of computational overhead
and performance impact than the standard Basic and Digest schemes.”
但是使用起來還是非常的方便的。因為 NTLM 連接是有狀態的,通常建議使用相對簡單的方法觸發NTLM 認證,比如GET或 HEAD, 而重用相同的連接來執行代價更大的方法,特別是它們包含請求實體,比如 POST或 PUT。
DefaultHttpClient httpclient = new DefaultHttpClient(); NTCredentials creds = new NTCredentials("user", "pwd", "myworkstation", "microsoft.com"); httpclient.getCredentialsProvider().setCredentials(AuthScop .ANY, creds); HttpHost target = new HttpHost("www.microsoft.com", 80, "http"); // 保證相同的內容來用于執行邏輯相關的請求 HttpContext localContext = new BasicHttpContext(); // 首先執行簡便的方法。這會觸發NTLM認證 HttpGet httpget = new HttpGet("/ntlm-protected/info"); HttpResponse response1 = httpclient.execute(target, httpget localContext); HttpEntity entity1 = response1.getEntity(); if (entity1 != null) { entity1.consumeContent(); } //之后使用相同的內容(和連接)執行開銷大的方法。 HttpPost httppost = new HttpPost("/ntlm-protected/form"); httppost.setEntity(new StringEntity("lots and lots of data")) HttpResponse response2 = httpclient.execute(target, httppost, localContext); HttpEntity entity2 = response2.getEntity(); if (entity2 != null) { entity2.consumeContent(); }
import org.apache.commons.httpclient.ProxyHost;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.ParseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.params.ConnRouteParams;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
DefaultHttpClient httpClient = new DefaultHttpClient();
String proxyHost = "";
int proxyPort = 8080;
String userName = "";
String password = "";
httpClient.getCredentialsProvider().setCredentials(
new AuthScope(proxyHost, proxyPort),
new UsernamePasswordCredentials(userName, password));
HttpHost proxy = new HttpHost(proxyHost, proxyPort);
httpClient.getParams().setParameter(ConnRouteParams.DEFAULT_PROXY,
proxy);
return httpClient;
}