少年阿賓

          那些青春的歲月

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks
          第四章 HTTP認(rèn)證
          HttpClient提供對由HTTP標(biāo)準(zhǔn)規(guī)范定義的認(rèn)證模式的完全支持。HttpClient的認(rèn)證框架可以擴(kuò)展支持非標(biāo)準(zhǔn)的認(rèn)證模式,比如NTLM和SPNEGO。

          4.1 用戶憑證

          任何用戶身份驗(yàn)證的過程都需要一組可以用于建立用戶身份的憑據(jù)。用戶憑證的最簡單的形式可以僅僅是用戶名/密碼對。UsernamePasswordCredentials代表了一組包含安全規(guī)則和明文密碼的憑據(jù)。這個(gè)實(shí)現(xiàn)對由HTTP標(biāo)準(zhǔn)規(guī)范中定義的標(biāo)準(zhǔn)認(rèn)證模式是足夠的

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

          輸出內(nèi)容為:

          user
          pwd

          NTCredentials是微軟Windows指定的實(shí)現(xiàn),它包含了除了用戶名/密碼對外,一組額外的Windows指定的屬性,比如用戶域名的名字,比如在微軟的Windows網(wǎng)絡(luò)中,相同的用戶使用不同設(shè)置的認(rèn)證可以屬于不同的域。

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

          輸出內(nèi)容為:

          DOMAIN/user
          pwd

          4.2 認(rèn)證模式

          AuthScheme接口代表了抽象的,面向挑戰(zhàn)-響應(yīng)的認(rèn)證模式。一個(gè)認(rèn)證模式期望支持如下的功能:
          • 解析和處理由目標(biāo)服務(wù)器在對受保護(hù)資源請求的響應(yīng)中發(fā)回的挑戰(zhàn)。
          • 提供處理挑戰(zhàn)的屬性:認(rèn)證模式類型和它的參數(shù),如果可用,比如這個(gè)認(rèn)證模型可應(yīng)用的領(lǐng)域。
          • 對給定的憑證組和HTTP請求對響應(yīng)真實(shí)認(rèn)證挑戰(zhàn)生成認(rèn)證字符串。
          要注意認(rèn)證模式可能是有狀態(tài)的,涉及一系列的挑戰(zhàn)-響應(yīng)交流。HttpClient附帶了一些AuthScheme實(shí)現(xiàn):
          • Basic(基本):Basic認(rèn)證模式定義在RFC 2617中。這個(gè)認(rèn)證模式是不安全的,因?yàn)閼{據(jù)以明文形式傳送。盡管它不安全,如果用在和TLS/SSL加密的組合中,Basic認(rèn)證模式是完全夠用的。
          • Digest(摘要):Digest認(rèn)證模式定義在RFC 2617中。Digest認(rèn)證模式比Basic有顯著的安全提升,對不想通過TLS/SL加密在完全運(yùn)輸安全上開銷的應(yīng)用程序來說也是很好的選擇。
          • NTLM:NTLM是一個(gè)由微軟開發(fā)的優(yōu)化Windows平臺(tái)的專有認(rèn)證模式。NTLM被認(rèn)為是比Digest更安全的模式。這個(gè)模式需要外部的NTLM引擎來工作。要獲取更多詳情請參考包含在HttpClient發(fā)布包中的NTLM_SUPPORT.txt文檔。

          4.3 HTTP認(rèn)證參數(shù)

          有一些可以用于定制HTTP認(rèn)證過程和獨(dú)立認(rèn)證模式行為的參數(shù):
          • 'http.protocol.handle-authentication':定義了是否認(rèn)證應(yīng)該被自動(dòng)處理。這個(gè)參數(shù)期望的得到一個(gè)java.lang.Boolean類型的值。如果這個(gè)參數(shù)沒有被設(shè)置,HttpClient將會(huì)自動(dòng)處理認(rèn)證。
          • 'http.auth.credential-charset':定義了當(dāng)編碼用戶憑證時(shí)使用的字符集。這個(gè)參數(shù)期望得到一個(gè)java.lang.String類型的值。如果這個(gè)參數(shù)沒有被設(shè)置,那么就會(huì)使用US-ASCII。

          4.4 認(rèn)證模式注冊表

          HttpClient使用AuthSchemeRegistry類維護(hù)一個(gè)可用的認(rèn)證模式的注冊表。對于每個(gè)默認(rèn)的下面的模式是注冊過的:
          • Basic:基本認(rèn)證模式
          • Digest:摘要認(rèn)證模式
          請注意NTLM模式?jīng)]有對每個(gè)默認(rèn)的進(jìn)行注冊。NTLM不能對每個(gè)默認(rèn)開啟是應(yīng)為許可和法律上的原因。要獲取更詳細(xì)的關(guān)于如何開啟NTLM支持的內(nèi)容請看這部分。

          4.5 憑據(jù)提供器

          憑據(jù)提供器意來維護(hù)一組用戶憑據(jù),還有能夠?qū)μ囟ㄕJ(rèn)證范圍生產(chǎn)用戶憑據(jù)。認(rèn)證范圍包括主機(jī)名,端口號,領(lǐng)域名稱和認(rèn)證模式名稱。當(dāng)使用憑據(jù)提供器來注冊憑據(jù)時(shí),我們可以提供一個(gè)通配符(任意主機(jī),任意端口,任意領(lǐng)域,任意模式)來替代確定的屬性值。如果直接匹配沒有發(fā)現(xiàn),憑據(jù)提供器期望被用來發(fā)現(xiàn)最匹配的特定范圍。

          HttpClient可以和任意實(shí)現(xiàn)了CredentialsProvider接口的憑據(jù)提供器的物理代表一同工作。默認(rèn)的CredentialsProvider實(shí)現(xiàn)被稱為BasicCredentialsProvider,它是簡單的憑借java.util.HashMap的實(shí)現(xiàn)。
          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")));

          輸出內(nèi)容為:

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

          4.6 HTTP認(rèn)證和執(zhí)行上下文

          HttpClient依賴于AuthState類來跟蹤關(guān)于認(rèn)證過程狀態(tài)的詳細(xì)信息。在HTTP請求執(zhí)行過程中,HttpClient創(chuàng)建2個(gè)AuthState的實(shí)例:一個(gè)對于目標(biāo)主機(jī)認(rèn)證,另外一個(gè)對于代理認(rèn)證。如果目標(biāo)服務(wù)器或代理需要用戶認(rèn)證,那么各自的AuthState實(shí)例將會(huì)被在認(rèn)證處理過程中使用的AuthScope,AuthScheme和Crednetials來填充。AuthState可以被檢查來找出請求的認(rèn)證是什么類型的,是否匹配AuthScheme的實(shí)現(xiàn),是否憑據(jù)提供器對給定的認(rèn)證范圍去找用戶憑據(jù)。

          在HTTP請求執(zhí)行的過程中,HttpClient添加了下列和認(rèn)證相關(guān)的對象到執(zhí)行上下文中:

          • 'http.authscheme-registry':AuthSchemeRegistry實(shí)例代表真實(shí)的認(rèn)證模式注冊表。在本地內(nèi)容中設(shè)置的這個(gè)屬性的值優(yōu)先于默認(rèn)的。
          • 'http.auth.credentials-provider':CookieSpec實(shí)例代表了真實(shí)的憑據(jù)提供器。在本地內(nèi)容中設(shè)置的這個(gè)屬性的值優(yōu)先于默認(rèn)的。
          • 'http.auth.target-scope':AuthState實(shí)例代表了真實(shí)的目標(biāo)認(rèn)證狀態(tài)。在本地內(nèi)容中設(shè)置的這個(gè)屬性的值優(yōu)先于默認(rèn)的。
          • 'http.auth.proxy-scope':AuthState實(shí)例代表了真實(shí)的代理認(rèn)證狀態(tài)。在本地內(nèi)容中設(shè)置的這個(gè)屬性的值優(yōu)先于默認(rèn)的。

          本地的HttpContext對象可以用于定制HTTP認(rèn)證內(nèi)容,并先于請求執(zhí)行或在請求被執(zhí)行之后檢查它的狀態(tài):

          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 搶占認(rèn)證

          HttpClient不支持開箱的搶占認(rèn)證,因?yàn)闉E用或重用不正確的搶占認(rèn)證可能會(huì)導(dǎo)致嚴(yán)重的安全問題,比如將用戶憑據(jù)以明文形式發(fā)送給未認(rèn)證的第三方。因此,用戶期望評估搶占認(rèn)證和在它們只能應(yīng)用程序環(huán)境內(nèi)容安全風(fēng)險(xiǎn)潛在的好處,而且要求使用如協(xié)議攔截器的標(biāo)準(zhǔn)HttpClient擴(kuò)展機(jī)制添加對搶占認(rèn)證的支持。

          這是一個(gè)簡單的協(xié)議攔截器,如果沒有企圖認(rèn)證,來搶先引入BasicScheme的實(shí)例到執(zhí)行上下文中。請注意攔截器必須在標(biāo)準(zhǔn)認(rèn)證攔截器之前加入到協(xié)議處理鏈中。

          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());
          // 獲得匹配目標(biāo)主機(jī)的憑據(jù)
          Credentials creds = credsProvider.getCredentials(authScope);
          // 如果發(fā)現(xiàn)了,搶先生成BasicScheme
          if (creds != null) {
          authState.setAuthScheme(new BasicScheme());
          authState.setCredentials(creds);
          }
          }
          }
          };
          DefaultHttpClient httpclient = new DefaultHttpClient();
          // 作為第一個(gè)攔截器加入到協(xié)議鏈中
          httpclient.addRequestInterceptor(preemptiveAuth, 0);

          4.8 NTLM 認(rèn)證

          當(dāng)前HttpClient沒有提對開箱的NTLM認(rèn)證模式的支持也可能永遠(yuǎn)也不會(huì)。這個(gè)原因是法律上的而不是技術(shù)上的。然而,NTLM認(rèn)證可以使用外部的NTLM引擎比如JCIFS[http://jcifs.samba.org/]來開啟,類庫由Samba[http://www.samba.org/]項(xiàng)目開發(fā),作為它們Windows的交互操作程序套裝的一部分。要獲取詳細(xì)內(nèi)容請參考HttpClient發(fā)行包中包含的NTLM_SUPPORT.txt文檔。

          4.8.1 NTLM連接持久化

          NTLM認(rèn)證模式是在計(jì)算開銷方面昂貴的多的,而且對標(biāo)準(zhǔn)的Basic和Digest模式的性能影響也很大。這很可能是為什么微軟選擇NTLM認(rèn)證模式為有狀態(tài)的主要原因之一。也就是說,一旦認(rèn)證通過,用戶標(biāo)識(shí)是和連接的整個(gè)生命周期相關(guān)聯(lián)的。NTLM連接的狀態(tài)特性使得連接持久化非常復(fù)雜,對于明顯的原因,持久化NTLM連接不能被使用不同用戶標(biāo)識(shí)的用戶重用。標(biāo)準(zhǔn)的連接管理器附帶HttpClient是完全能夠管理狀態(tài)連接的。而邏輯相關(guān)的,使用同一session和執(zhí)行上下文為了讓它們了解到當(dāng)前的用戶標(biāo)識(shí)的請求也是極為重要的。否則,HttpClient將會(huì)終止對每個(gè)基于NTLM保護(hù)資源的HTTP請求創(chuàng)建新的HTTP連接。要獲取關(guān)于有狀態(tài)的HTTP連接的詳細(xì)討論,請參考這個(gè)部分。

          因?yàn)镹TLM連接是有狀態(tài)的,通常建議使用相對簡單的方法觸發(fā)NTLM認(rèn)證,比如GET或HEAD,而重用相同的連接來執(zhí)行代價(jià)更大的方法,特別是它們包含請求實(shí)體,比如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");
          // 保證相同的內(nèi)容來用于執(zhí)行邏輯相關(guān)的請求
          HttpContext localContext = new BasicHttpContext();
          // 首先執(zhí)行簡便的方法。這會(huì)觸發(fā)NTLM認(rèn)證
          HttpGet httpget = new HttpGet("/ntlm-protected/info");
          HttpResponse response1 = httpclient.execute(target, httpget, localContext);
          HttpEntity entity1 = response1.getEntity();
          if (entity1 != null) {
          entity1.consumeContent();
          }
          //之后使用相同的內(nèi)容(和連接)執(zhí)行開銷大的方法。
          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();
          }

           



          轉(zhuǎn)載自:http://www.cnblogs.com/loveyakamoz/archive/2011/07/21/2113247.html
          posted on 2012-09-26 16:44 abin 閱讀(835) 評論(0)  編輯  收藏 所屬分類: httpClient
          主站蜘蛛池模板: 武威市| 乌什县| 宣恩县| 本溪市| 乐清市| 凭祥市| 英山县| 南和县| 河东区| 东山县| 永平县| 霸州市| 南部县| 洛川县| 鹤岗市| 滕州市| 岳阳县| 松溪县| 武夷山市| 旌德县| 信丰县| 滕州市| 和龙市| 日照市| 文登市| 会昌县| 如东县| 刚察县| 隆子县| 汨罗市| 铁力市| 金塔县| 渭南市| 许昌县| 兴化市| 竹山县| 高邑县| 昆山市| 博爱县| 北碚区| 宣化县|