HttpClient 教程 (四)

第四章 HTTP認證java

HttpClient提供對由HTTP標準規範定義的認證模式的徹底支持。HttpClient的認證框架能夠擴展支持非標準的認證模式,好比NTLM和SPNEGO。

4.1 用戶憑證

任何用戶身份驗證的過程都須要一組能夠用於創建用戶身份的憑據。用戶憑證的最簡單的形式能夠僅僅是用戶名/密碼對。UsernamePasswordCredentials表明了一組包含安全規則和明文密碼的憑據。這個實現對由HTTP標準規範中定義的標準認證模式是足夠的安全

UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());

輸出內容爲:服務器

user
pwd

NTCredentials是微軟Windows指定的實現,它包含了除了用戶名/密碼對外,一組額外的Windows指定的屬性,好比用戶域名的名字,好比在微軟的Windows網絡中,相同的用戶使用不一樣設置的認證能夠屬於不一樣的域。網絡

NTCredentials creds = new NTCredentials("user", "pwd", "workstation", "domain");
System.out.println(creds.getUserPrincipal().getName());
System.out.println(creds.getPassword());

輸出內容爲:session

DOMAIN/user
pwd

4.2 認證模式

AuthScheme接口表明了抽象的,面向挑戰-響應的認證模式。一個認證模式指望支持以下的功能:
  • 解析和處理由目標服務器在對受保護資源請求的響應中發回的挑戰。
  • 提供處理挑戰的屬性:認證模式類型和它的參數,若是可用,好比這個認證模型可應用的領域。
  • 對給定的憑證組和HTTP請求對響應真實認證挑戰生成認證字符串。
要注意認證模式多是有狀態的,涉及一系列的挑戰-響應交流。HttpClient附帶了一些AuthScheme實現:
  • Basic(基本):Basic認證模式定義在RFC 2617中。這個認證模式是不安全的,由於憑據以明文形式傳送。儘管它不安全,若是用在和TLS/SSL加密的組合中,Basic認證模式是徹底夠用的。
  • Digest(摘要):Digest認證模式定義在RFC 2617中。Digest認證模式比Basic有顯著的安全提高,對不想經過TLS/SL加密在徹底運輸安全上開銷的應用程序來講也是很好的選擇。
  • NTLM:NTLM是一個由微軟開發的優化Windows平臺的專有認證模式。NTLM被認爲是比Digest更安全的模式。這個模式須要外部的NTLM引擎來工做。要獲取更多詳情請參考包含在HttpClient發佈包中的NTLM_SUPPORT.txt文檔。

4.3 HTTP認證參數

有一些能夠用於定製HTTP認證過程和獨立認證模式行爲的參數:
  • 'http.protocol.handle-authentication':定義了是否定證應該被自動處理。這個參數指望的獲得一個java.lang.Boolean類型的值。若是這個參數沒有被設置,HttpClient將會自動處理認證。
  • 'http.auth.credential-charset':定義了當編碼用戶憑證時使用的字符集。這個參數指望獲得一個java.lang.String類型的值。若是這個參數沒有被設置,那麼就會使用US-ASCII。

4.4 認證模式註冊表

HttpClient使用AuthSchemeRegistry類維護一個可用的認證模式的註冊表。對於每一個默認的下面的模式是註冊過的:
  • Basic:基本認證模式
  • Digest:摘要認證模式
請注意NTLM模式沒有對每一個默認的進行註冊。NTLM不能對每一個默認開啓是應爲許可和法律上的緣由。要獲取更詳細的關於如何開啓NTLM支持的內容請看這部分。

4.5 憑據提供器

憑據提供器意來維護一組用戶憑據,還有可以對特定認證範圍生產用戶憑據。認證範圍包括主機名,端口號,領域名稱和認證模式名稱。當使用憑據提供器來註冊憑據時,咱們能夠提供一個通配符(任意主機,任意端口,任意領域,任意模式)來替代肯定的屬性值。若是直接匹配沒有發現,憑據提供器指望被用來發現最匹配的特定範圍。框架

HttpClient能夠和任意實現了CredentialsProvider接口的憑據提供器的物理表明一同工做。默認的CredentialsProvider實現被稱爲BasicCredentialsProvider,它是簡單的憑藉java.util.HashMap的實現。
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("somehost", AuthScope.ANY_PORT),
new UsernamePasswordCredentials("u1", "p1"));
credsProvider.setCredentials(
new AuthScope("somehost", 8080),
new UsernamePasswordCredentials("u2", "p2"));
credsProvider.setCredentials(
new AuthScope("otherhost", 8080, AuthScope.ANY_REALM, "ntlm"),
new UsernamePasswordCredentials("u3", "p3"));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 80, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("somehost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, "realm", "basic")));
System.out.println(credsProvider.getCredentials(
new AuthScope("otherhost", 8080, null, "ntlm")));

輸出內容爲:dom

[principal: u1]
[principal: u2]
null
[principal: u3]

4.6 HTTP認證和執行上下文

HttpClient依賴於AuthState類來跟蹤關於認證過程狀態的詳細信息。在HTTP請求執行過程當中,HttpClient建立2個AuthState的實例:一個對於目標主機認證,另一個對於代理認證。若是目標服務器或代理須要用戶認證,那麼各自的AuthState實例將會被在認證處理過程當中使用的AuthScope,AuthScheme和Crednetials來填充。AuthState能夠被檢查來找出請求的認證是什麼類型的,是否匹配AuthScheme的實現,是否憑據提供器對給定的認證範圍去找用戶憑據。ide

在HTTP請求執行的過程當中,HttpClient添加了下列和認證相關的對象到執行上下文中:post

  • 'http.authscheme-registry':AuthSchemeRegistry實例表明真實的認證模式註冊表。在本地內容中設置的這個屬性的值優先於默認的。
  • 'http.auth.credentials-provider':CookieSpec實例表明了真實的憑據提供器。在本地內容中設置的這個屬性的值優先於默認的。
  • 'http.auth.target-scope':AuthState實例表明了真實的目標認證狀態。在本地內容中設置的這個屬性的值優先於默認的。
  • 'http.auth.proxy-scope':AuthState實例表明了真實的代理認證狀態。在本地內容中設置的這個屬性的值優先於默認的。

本地的HttpContext對象能夠用於定製HTTP認證內容,並先於請求執行或在請求被執行以後檢查它的狀態:性能

HttpClient httpclient = new DefaultHttpClient();
HttpContext localContext = new BasicHttpContext();
HttpGet httpget = new HttpGet("http://localhost:8080/");
HttpResponse response = httpclient.execute(httpget, localContext);
AuthState proxyAuthState = (AuthState) localContext.getAttribute(
ClientContext.PROXY_AUTH_STATE);
System.out.println("Proxy auth scope: " + proxyAuthState.getAuthScope());
System.out.println("Proxy auth scheme: " + proxyAuthState.getAuthScheme());
System.out.println("Proxy auth credentials: " + proxyAuthState.getCredentials());
AuthState targetAuthState = (AuthState) localContext.getAttribute(
ClientContext.TARGET_AUTH_STATE);
System.out.println("Target auth scope: " + targetAuthState.getAuthScope());
System.out.println("Target auth scheme: " + targetAuthState.getAuthScheme());
System.out.println("Target auth credentials: " + targetAuthState.getCredentials());

4.7 搶佔認證

HttpClient不支持開箱的搶佔認證,由於濫用或重用不正確的搶佔認證可能會致使嚴重的安全問題,好比將用戶憑據以明文形式發送給未認證的第三方。所以,用戶指望評估搶佔認證和在它們只能應用程序環境內容安全風險潛在的好處,並且要求使用如協議攔截器的標準HttpClient擴展機制添加對搶佔認證的支持。

這是一個簡單的協議攔截器,若是沒有企圖認證,來搶先引入BasicScheme的實例到執行上下文中。請注意攔截器必須在標準認證攔截器以前加入到協議處理鏈中。

HttpRequestInterceptor preemptiveAuth = new HttpRequestInterceptor() {
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);
// 若是沒有初始化auth模式
if (authState.getAuthScheme() == null) {
AuthScope authScope = new AuthScope(
targetHost.getHostName(),
targetHost.getPort());
// 得到匹配目標主機的憑據
Credentials creds = credsProvider.getCredentials(authScope);
// 若是發現了,搶先生成BasicScheme
if (creds != null) {
authState.setAuthScheme(new BasicScheme());
authState.setCredentials(creds);
}
}
}
};
DefaultHttpClient httpclient = new DefaultHttpClient();
// 做爲第一個攔截器加入到協議鏈中
httpclient.addRequestInterceptor(preemptiveAuth, 0);

4.8 NTLM 認證

當前HttpClient沒有提對開箱的NTLM認證模式的支持也可能永遠也不會。這個緣由是法律上的而不是技術上的。然而,NTLM認證可使用外部的NTLM引擎好比JCIFS[http://jcifs.samba.org/]來開啓,類庫由Samba[http://www.samba.org/]項目開發,做爲它們Windows的交互操做程序套裝的一部分。要獲取詳細內容請參考HttpClient發行包中包含的NTLM_SUPPORT.txt文檔。

4.8.1 NTLM鏈接持久化

NTLM認證模式是在計算開銷方面昂貴的多的,並且對標準的Basic和Digest模式的性能影響也很大。這極可能是爲何微軟選擇NTLM認證模式爲有狀態的主要緣由之一。也就是說,一旦認證經過,用戶標識是和鏈接的整個生命週期相關聯的。NTLM鏈接的狀態特性使得鏈接持久化很是複雜,對於明顯的緣由,持久化NTLM鏈接不能被使用不一樣用戶標識的用戶重用。標準的鏈接管理器附帶HttpClient是徹底可以管理狀態鏈接的。而邏輯相關的,使用同一session和執行上下文爲了讓它們瞭解到當前的用戶標識的請求也是極爲重要的。不然,HttpClient將會終止對每一個基於NTLM保護資源的HTTP請求建立新的HTTP鏈接。要獲取關於有狀態的HTTP鏈接的詳細討論,請參考這個部分。

由於NTLM鏈接是有狀態的,一般建議使用相對簡單的方法觸發NTLM認證,好比GET或HEAD,而重用相同的鏈接來執行代價更大的方法,特別是它們包含請求實體,好比POST或PUT。

DefaultHttpClient httpclient = new DefaultHttpClient();
NTCredentials creds = new NTCredentials("user", "pwd", "myworkstation", "microsoft.com");
httpclient.getCredentialsProvider().setCredentials(AuthScope.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();
}
相關文章
相關標籤/搜索