第四章 HTTP認證java
任何用戶身份驗證的過程都須要一組能夠用於創建用戶身份的憑據。用戶憑證的最簡單的形式能夠僅僅是用戶名/密碼對。UsernamePasswordCredentials表明了一組包含安全規則和明文密碼的憑據。這個實現對由HTTP標準規範中定義的標準認證模式是足夠的安全
UsernamePasswordCredentials creds = new UsernamePasswordCredentials("user", "pwd");System.out.println(creds.getUserPrincipal().getName());System.out.println(creds.getPassword());
輸出內容爲:服務器
userpwd
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/userpwd
憑據提供器意來維護一組用戶憑據,還有可以對特定認證範圍生產用戶憑據。認證範圍包括主機名,端口號,領域名稱和認證模式名稱。當使用憑據提供器來註冊憑據時,咱們能夠提供一個通配符(任意主機,任意端口,任意領域,任意模式)來替代肯定的屬性值。若是直接匹配沒有發現,憑據提供器指望被用來發現最匹配的特定範圍。框架
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]
HttpClient依賴於AuthState類來跟蹤關於認證過程狀態的詳細信息。在HTTP請求執行過程當中,HttpClient建立2個AuthState的實例:一個對於目標主機認證,另一個對於代理認證。若是目標服務器或代理須要用戶認證,那麼各自的AuthState實例將會被在認證處理過程當中使用的AuthScope,AuthScheme和Crednetials來填充。AuthState能夠被檢查來找出請求的認證是什麼類型的,是否匹配AuthScheme的實現,是否憑據提供器對給定的認證範圍去找用戶憑據。ide
在HTTP請求執行的過程當中,HttpClient添加了下列和認證相關的對象到執行上下文中:post
本地的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());
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);// 若是發現了,搶先生成BasicSchemeif (creds != null) {authState.setAuthScheme(new BasicScheme());authState.setCredentials(creds);}}}};DefaultHttpClient httpclient = new DefaultHttpClient();// 做爲第一個攔截器加入到協議鏈中httpclient.addRequestInterceptor(preemptiveAuth, 0);
當前HttpClient沒有提對開箱的NTLM認證模式的支持也可能永遠也不會。這個緣由是法律上的而不是技術上的。然而,NTLM認證可使用外部的NTLM引擎好比JCIFS[http://jcifs.samba.org/]來開啓,類庫由Samba[http://www.samba.org/]項目開發,做爲它們Windows的交互操做程序套裝的一部分。要獲取詳細內容請參考HttpClient發行包中包含的NTLM_SUPPORT.txt文檔。
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();}