HttpClient:是一個接口
首先需要先創建一個DefaultHttpClient的實例
HttpClient httpClient=new DefaultHttpClient();
發送GET請求:
先創建一個HttpGet對象,傳入目標的網絡地址,然后調用HttpClient的execute()方法即可:
HttpGet HttpGet=new HttpGet(“http://www.baidu.com”);
httpClient.execute(httpGet);
發送POST請求:
創建一個HttpPost對象,傳入目標的網絡地址:
HttpPost httpPost=new HttpPost(“http://www.baidu.com”);
通過一個NameValuePair集合來存放待提交的參數,並將這個參數集合傳入到一個UrlEncodedFormEntity中,然后調用HttpPost的setEntity()方法將構建好的UrlEncodedFormEntity傳入:
List<NameValuePair>params=newArrayList<NameValuePair>();
Params.add(new BasicNameValuePair(“username”,”admin”));
Params.add(new BasicNameValuePair(“password”,”123456”));
UrlEncodedFormEntity entity=newUrlEncodedFormEntity(params,”utf-8”);
httpPost.setEntity(entity);
調用HttpClient的execute()方法,並將HttpPost對象傳入即可:
HttpClient.execute(HttpPost);
執行execute()方法之后會返回一個HttpResponse對象,服務器所返回的所有信息就保護在HttpResponse里面.
先取出服務器返回的狀態碼,如果等於200就說明請求和響應都成功了:
If(httpResponse.getStatusLine().getStatusCode()==200){
//請求和響應都成功了
HttpEntityentity=HttpResponse.getEntity();//調用getEntity()方法獲取到一個HttpEntity實例
Stringresponse=EntityUtils.toString(entity,”utf-8”);//用EntityUtils.toString()這個靜態方法將HttpEntity轉換成字符串,防止服務器返回的數據帶有中文,所以在轉換的時候將字符集指定成utf-8就可以了
}
Http協議的重要性相信不用我多說了,HttpClient相比傳統JDK自帶的URLConnection,增加了易用性和靈活性(具體區別,日后我們再討論),它不僅是客戶端發送Http請求變得容易,而且也方便了開發人員測試接口(基於Http協議的),即提高了開發的效率,也方便提高代碼的健壯性。因此熟練掌握HttpClient是很重要的必修內容,掌握HttpClient后,相信對於Http協議的了解會更加深入。
一、簡介
HttpClient是Apache Jakarta Common下的子項目,用來提供高效的、最新的、功能豐富的支持HTTP協議的客戶端編程工具包,並且它支持HTTP協議最新的版本和建議。HttpClient已經應用在很多的項目中,比如Apache Jakarta上很著名的另外兩個開源項目Cactus和HTMLUnit都使用了HttpClient。
下載地址: http://hc.apache.org/downloads.cgi
二、特性
1. 基於標准、純凈的java語言。實現了Http1.0和Http1.1
2. 以可擴展的面向對象的結構實現了Http全部的方法(GET, POST, PUT, DELETE, HEAD, OPTIONS, and TRACE)。
3. 支持HTTPS協議。
4. 通過Http代理建立透明的連接。
5. 利用CONNECT方法通過Http代理建立隧道的https連接。
6. Basic, Digest, NTLMv1, NTLMv2, NTLM2 Session, SNPNEGO/Kerberos認證方案。
7. 插件式的自定義認證方案。
8. 便攜可靠的套接字工廠使它更容易的使用第三方解決方案。
9. 連接管理器支持多線程應用。支持設置最大連接數,同時支持設置每個主機的最大連接數,發現並關閉過期的連接。
10. 自動處理Set-Cookie中的Cookie。
11. 插件式的自定義Cookie策略。
12. Request的輸出流可以避免流中內容直接緩沖到socket服務器。
13. Response的輸入流可以有效的從socket服務器直接讀取相應內容。
14. 在http1.0和http1.1中利用KeepAlive保持持久連接。
15. 直接獲取服務器發送的response code和 headers。
16. 設置連接超時的能力。
17. 實驗性的支持http1.1 response caching。
18. 源代碼基於Apache License 可免費獲取。
三、使用方法
使用HttpClient發送請求、接收響應很簡單,一般需要如下幾步即可。
1. 創建HttpClient對象。
2. 創建請求方法的實例,並指定請求URL。如果需要發送GET請求,創建HttpGet對象;如果需要發送POST請求,創建HttpPost對象。
3. 如果需要發送請求參數,可調用HttpGet、HttpPost共同的setParams(HetpParams params)方法來添加請求參數;對於HttpPost對象而言,也可調用setEntity(HttpEntity entity)方法來設置請求參數。
4. 調用HttpClient對象的execute(HttpUriRequest request)發送請求,該方法返回一個HttpResponse。
5. 調用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可獲取服務器的響應頭;調用HttpResponse的getEntity()方法可獲取HttpEntity對象,該對象包裝了服務器的響應內容。程序可通過該對象獲取服務器的響應內容。
6. 釋放連接。無論執行方法是否成功,都必須釋放連接
四、實例
1 package com.fico.rma.service; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.net.URL; 7 import org.apache.commons.io.FileUtils; 8 import org.apache.http.HttpEntity; 9 import org.apache.http.HttpResponse; 10 import org.apache.http.HttpStatus; 11 import org.apache.http.client.methods.HttpPost; 12 import org.apache.http.entity.InputStreamEntity; 13 import org.apache.http.impl.client.DefaultHttpClient; 14 import com.fico.rma.RmaConfig; 15 import com.fico.rma.util.RmaConstants; 16 17 public class ReleaseService { 18 19 private final static String SEP=File.separator; 20 21 public static void createRelease(String releaseName,String projectName) throws IOException{ 22 String dir=RmaConfig.getString("basepath"); 23 projectName = projectName.toLowerCase(); 24 File destAdbFile=new File(dir+SEP+RmaConstants.RELEASE_DIR+SEP+releaseName+SEP+projectName+".adb"); 25 File srcAdbFile = null; 26 if("dldsunproject".equals(projectName.toLowerCase().toString().trim())){ 27 srcAdbFile=new File(dir+SEP+RmaConstants.ADB_DIR+SEP+"DldsUnProject"+SEP+"adb"+SEP+"Pingan.server_service_0.adb"); 28 }else { 29 srcAdbFile=new File(dir+SEP+RmaConstants.ADB_DIR+SEP+"PingAnProject"+SEP+"adb"+SEP+"Pingan.server_service_0.adb"); 30 } 31 FileUtils.copyFile(srcAdbFile, destAdbFile); 32 } 33 34 public static void pulishRelease(String adbFile,String url,String projectName) throws IOException { 35 36 DefaultHttpClient httpclient = new DefaultHttpClient(); 37 38 File file = new File(adbFile); 39 InputStreamEntity reqEntity = new InputStreamEntity(new FileInputStream(file), -1); 40 reqEntity.setContentType("binary/octet-stream"); 41 reqEntity.setChunked(true); 42 43 HttpPost httppost = new HttpPost(url); 44 httppost.setEntity(reqEntity); 45 httppost.setHeader("projectName", projectName); 46 47 HttpResponse response = httpclient.execute(httppost); 48 //System.out.println("response.getStatusLine().getStatusCode():"+response.getStatusLine().getStatusCode()); 49 50 if (HttpStatus.SC_OK == response.getStatusLine().getStatusCode()) { 51 HttpEntity enty = response.getEntity(); 52 } 53 } 54 55 56 }
以上是發送端的代碼,下面是接受端的代碼:
1 package com.pingan.res; 2 3 import java.io.File; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.io.OutputStream; 8 import java.io.PrintWriter; 9 10 import javax.servlet.ServletContext; 11 import javax.servlet.ServletException; 12 import javax.servlet.annotation.WebServlet; 13 import javax.servlet.http.HttpServlet; 14 import javax.servlet.http.HttpServletRequest; 15 import javax.servlet.http.HttpServletResponse; 16 import com.blazesoft.server.local.NdLocalServerException; 17 18 /** 19 * Servlet implementation class ReleaseServlet 20 */ 21 @WebServlet("/Release") 22 public class ReleaseServlet extends HttpServlet { 23 private static final long serialVersionUID = 1L; 24 25 /** 26 * @see HttpServlet#HttpServlet() 27 */ 28 public ReleaseServlet() { 29 super(); 30 // TODO Auto-generated constructor stub 31 } 32 33 /** 34 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse 35 * response) 36 */ 37 protected void doPost(HttpServletRequest request, 38 HttpServletResponse response) throws ServletException, IOException { 39 40 /* 41 * 功能點:RMA這發布adb包的時候的res調用的入口點。 42 * 功能1:獲取到RMA中具體是哪個項目 43 * 功能2:根據項目把adb包和對應的.server文件拷貝到不同的目錄下(目錄名稱為RMA的項目名稱的小寫) 44 * 功能3:重新加載相對應的adb文件到對應的server中 45 */ 46 47 //功能1 48 String projectName =""; 49 projectName =request.getHeader("projectName"); 50 projectName = projectName.toLowerCase(); 51 String sep=File.separator; 52 // String dir=ResConfig.getString("basepath"); 53 String dir = ""; 54 dir=ResConfig.getProperties().getProperty("basepath"); 55 56 //功能2 57 String adbFile=dir+sep+"production"+sep+projectName+sep+projectName+".adb"; 58 String serverFile = dir+sep+"production"+sep+projectName+sep+projectName+".server"; 59 OutputStream out = new FileOutputStream(adbFile); 60 61 InputStream in = request.getInputStream(); 62 63 int length = 0; 64 byte[] buf = new byte[1024]; 65 66 while ((length = in.read(buf)) != -1) { 67 out.write(buf, 0, length); 68 } 69 70 in.close(); 71 out.close(); 72 73 //功能3:重啟server 74 reloadRuleServer(request,projectName,serverFile); 75 76 77 response.setContentType("text/xml"); 78 PrintWriter pw=response.getWriter(); 79 80 pw.write("<Response><Result>OK</Result></Response>"); 81 82 pw.flush(); 83 84 } 85 86 private void reloadRuleServer(HttpServletRequest req,String projectName,String serverFile) throws ServletException{ 87 projectName = projectName.toLowerCase(); 88 String projectDefinition1 = ""; 89 if (!"".equals(projectName)&&projectName.length()>0) { 90 projectDefinition1 = ResConfig.getProperties().getProperty(projectName+"_df"); 91 } 92 93 Server server = null; 94 ServletContext context=req.getServletContext(); 95 server = (Server)context.getAttribute(projectName+"Server"); 96 97 try{ 98 if(server==null){ 99 server = (Server)Server.createServer(serverFile); 100 context.setAttribute(projectName+"Server", server); 101 } 102 server.resetService(projectDefinition1); 103 PriorRunner runner=new PriorRunner(server,100); 104 runner.run(); 105 106 107 } catch (NdLocalServerException e) { 108 e.printStackTrace(); 109 throw new ServletException(e.getMessage()); 110 } 111 112 } 113 114 }
-
- HttpClient 是 Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,並且它支持 HTTP 協議最新的版本和建議。本文首先介紹 HTTPClient,然后根據作者實際工作經驗給出了一些常見問題的解決方法。HTTP 協議可能是現在 Internet 上使用得最多、最重要的協議了,越來越多的 Java 應用程序需要直接通過 HTTP 協議來訪問網絡資源。雖然在 JDK 的 java.net 包中已經提供了訪問 HTTP 協議的基本功能,但是對於大部分應用程序來說,JDK 庫本身提供的功能還不夠豐富和靈活。HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,並且它支持 HTTP 協議最新的版本和建議。HttpClient 已經應用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient。現在HttpClient最新版本為 HttpClient 4.0-beta2
2.HttpClient 功能介紹
以下列出的是 HttpClient 提供的主要的功能,要知道更多詳細的功能可以參見 HttpClient 的主頁。
(1)實現了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
(2)支持自動轉向
(3)支持 HTTPS 協議
(4)支持代理服務器等
3.HttpClient 基本功能的使用
(1) GET方法
使用 HttpClient 需要以下 6 個步驟:
1. 創建 HttpClient 的實例
2. 創建某種連接方法的實例,在這里是 GetMethod。在 GetMethod 的構造函數中傳入待連接的地址
3. 調用第一步中創建好的實例的 execute 方法來執行第二步中創建好的 method 實例
4. 讀 response
5. 釋放連接。無論執行方法是否成功,都必須釋放連接
6. 對得到后的內容進行處理
根據以上步驟,我們來編寫用GET方法來取得某網頁內容的代碼。
大部分情況下 HttpClient 默認的構造函數已經足夠使用。 HttpClient httpClient = new HttpClient();
創建GET方法的實例。在GET方法的構造函數中傳入待連接的地址即可。用GetMethod將會自動處理轉發過程,如果想要把自動處理轉發過程去掉的話,可以調用方法setFollowRedirects(false)。 GetMethod getMethod = new GetMethod(".....");
調用實例httpClient的executeMethod方法來執行getMethod。由於是執行在網絡上的程序,在運行executeMethod方法的時候,需要處理兩個異常,分別是HttpException和IOException。引起第一種異常的原因主要可能是在構造getMethod的時候傳入的協議不對,比如不小心將"http"寫成"htp",或者服務器端返回的內容不正常等,並且該異常發生是不可恢復的;第二種異常一般是由於網絡原因引起的異常,對於這種異常 (IOException),HttpClient會根據你指定的恢復策略自動試着重新執行executeMethod方法。HttpClient的恢復策略可以自定義(通過實現接口HttpMethodRetryHandler來實現)。通過httpClient的方法setParameter設置你實現的恢復策略,本文中使用的是系統提供的默認恢復策略,該策略在碰到第二類異常的時候將自動重試3次。executeMethod返回值是一個整數,表示了執行該方法后服務器返回的狀態碼,該狀態碼能表示出該方法執行是否成功、需要認證或者頁面發生了跳轉(默認狀態下GetMethod的實例是自動處理跳轉的)等。 //設置成了默認的恢復策略,在發生異常時候將自動重試3次,在這里你也可以設置成自定義的恢復策略
1 getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 2 new DefaultHttpMethodRetryHandler()); 3 //執行getMethod 4 int statusCode = client.executeMethod(getMethod); 5 if (statusCode != HttpStatus.SC_OK) { 6 System.err.println("Method failed: " + getMethod.getStatusLine()); 7 }
- HttpClient 是 Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,並且它支持 HTTP 協議最新的版本和建議。本文首先介紹 HTTPClient,然后根據作者實際工作經驗給出了一些常見問題的解決方法。HTTP 協議可能是現在 Internet 上使用得最多、最重要的協議了,越來越多的 Java 應用程序需要直接通過 HTTP 協議來訪問網絡資源。雖然在 JDK 的 java.net 包中已經提供了訪問 HTTP 協議的基本功能,但是對於大部分應用程序來說,JDK 庫本身提供的功能還不夠豐富和靈活。HttpClient 是 Apache Jakarta Common 下的子項目,用來提供高效的、最新的、功能豐富的支持 HTTP 協議的客戶端編程工具包,並且它支持 HTTP 協議最新的版本和建議。HttpClient 已經應用在很多的項目中,比如 Apache Jakarta 上很著名的另外兩個開源項目 Cactus 和 HTMLUnit 都使用了 HttpClient。現在HttpClient最新版本為 HttpClient 4.0-beta2
在返回的狀態碼正確后,即可取得內容。取得目標地址的內容有三種方法:第一種,getResponseBody,該方法返回的是目標的二進制的byte流;第二種,getResponseBodyAsString,這個方法返回的是String類型,值得注意的是該方法返回的String的編碼是根據系統默認的編碼方式,所以返回的String值可能編碼類型有誤,在本文的"字符編碼"部分中將對此做詳細介紹;第三種,getResponseBodyAsStream,這個方法對於目標地址中有大量數據需要傳輸是最佳的。在這里我們使用了最簡單的getResponseBody方法。 byte[] responseBody = method.getResponseBody();
釋放連接。無論執行方法是否成功,都必須釋放連接。 method.releaseConnection();
處理內容。在這一步中根據你的需要處理內容,在例子中只是簡單的將內容打印到控制台。 System.out.println(new String(responseBody));
下面是程序的完整代碼:
1 package test; 2 import java.io.IOException; 3 import org.apache.commons.httpclient.*; 4 import org.apache.commons.httpclient.methods.GetMethod; 5 import org.apache.commons.httpclient.params.HttpMethodParams; 6 public class GetSample{ 7 public static void main(String[] args) { 8 //構造HttpClient的實例 9 HttpClient httpClient = new HttpClient(); 10 //創建GET方法的實例 11 GetMethod getMethod = new GetMethod("..."); 12 //使用系統提供的默認的恢復策略 13 getMethod.getParams().setParameter(HttpMethodParams.RETRY_HANDLER, 14 new DefaultHttpMethodRetryHandler()); 15 try { 16 //執行getMethod 17 int statusCode = httpClient.executeMethod(getMethod); 18 if (statusCode != HttpStatus.SC_OK) { 19 System.err.println("Method failed: " 20 + getMethod.getStatusLine()); 21 } 22 //讀取內容 23 byte[] responseBody = getMethod.getResponseBody(); 24 //處理內容 25 System.out.println(new String(responseBody)); 26 } catch (HttpException e) { 27 //發生致命的異常,可能是協議不對或者返回的內容有問題 28 System.out.println("Please check your provided http address!"); 29 e.printStackTrace(); 30 } catch (IOException e) { 31 //發生網絡異常 32 e.printStackTrace(); 33 } finally { 34 //釋放連接 35 getMethod.releaseConnection(); 36 } 37 } 38 }
(2)POST方法
根據RFC2616,對POST的解釋如下:POST方法用來向目的服務器發出請求,要求它接受被附在請求后的實體,並把它當作請求隊列(Request-Line)中請求URI所指定資源的附加新子項。POST被設計成用統一的方法實現下列功能:
對現有資源的注釋(Annotation of existing resources)
向電子公告欄、新聞組,郵件列表或類似討論組發送消息
提交數據塊,如將表單的結果提交給數據處理過程
通過附加操作來擴展數據庫
調用HttpClient中的PostMethod與GetMethod類似,除了設置PostMethod的實例與GetMethod有些不同之外,剩下的步驟都差不多。在下面的例子中,省去了與GetMethod相同的步驟,只說明與上面不同的地方,並以登錄清華大學BBS為例子進行說明。
構造PostMethod之前的步驟都相同,與GetMethod一樣,構造PostMethod也需要一個URI參數。在創建了PostMethod的實例之后,需要給method實例填充表單的值,在BBS的登錄表單中需要有兩個域,第一個是用戶名(域名叫id),第二個是密碼(域名叫passwd)。表單中的域用類NameValuePair來表示,該類的構造函數第一個參數是域名,第二參數是該域的值;將表單所有的值設置到PostMethod中用方法setRequestBody。另外由於BBS登錄成功后會轉向另外一個頁面,但是HttpClient對於要求接受后繼服務的請求,比如POST和PUT,不支持自動轉發,因此需要自己對頁面轉向做處理。具體的頁面轉向處理請參見下面的"自動轉向"部分。代碼如下:
1 String url = "...."; 2 PostMethod postMethod = new PostMethod(url); 3 // 填入各個表單域的值 4 NameValuePair[] data = { new NameValuePair("id", "youUserName"), 5 new NameValuePair("passwd", "yourPwd") }; 6 // 將表單的值放入postMethod中 7 postMethod.setRequestBody(data); 8 // 執行postMethod 9 int statusCode = httpClient.executeMethod(postMethod); 10 // HttpClient對於要求接受后繼服務的請求,象POST和PUT等不能自動處理轉發 11 // 301或者302 12 if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || 13 statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { 14 // 從頭中取出轉向的地址 15 Header locationHeader = postMethod.getResponseHeader("location"); 16 String location = null; 17 if (locationHeader != null) { 18 location = locationHeader.getValue(); 19 System.out.println("The page was redirected to:" + location); 20 } else { 21 System.err.println("Location field value is null."); 22 } 23 return;
4 使用HttpClient過程中常見的一些問題
下面介紹在使用HttpClient過程中常見的一些問題。
字符編碼
某目標頁的編碼可能出現在兩個地方,第一個地方是服務器返回的http頭中,另外一個地方是得到的html/xml頁面中。
在http頭的Content-Type字段可能會包含字符編碼信息。例如可能返回的頭會包含這樣子的信息:Content-Type: text/html; charset=UTF-8。這個頭信息表明該頁的編碼是UTF-8,但是服務器返回的頭信息未必與內容能匹配上。比如對於一些雙字節語言國家,可能服務器返回的編碼類型是UTF-8,但真正的內容卻不是UTF-8編碼的,因此需要在另外的地方去得到頁面的編碼信息;但是如果服務器返回的編碼不是UTF-8,而是具體的一些編碼,比如gb2312等,那服務器返回的可能是正確的編碼信息。通過method對象的getResponseCharSet()方法就可以得到http頭中的編碼信息。
對於象xml或者html這樣的文件,允許作者在頁面中直接指定編碼類型。比如在html中會有<meta http-equiv="Content-Type" content="text/html; charset=gb2312"/>這樣的標簽;或者在xml中會有<?xml version="1.0" encoding="gb2312"?>這樣的標簽,在這些情況下,可能與http頭中返回的編碼信息沖突,需要用戶自己判斷到底那種編碼類型應該是真正的編碼。
自動轉向
根據RFC2616中對自動轉向的定義,主要有兩種:301和302。301表示永久的移走(Moved Permanently),當返回的是301,則表示請求的資源已經被移到一個固定的新地方,任何向該地址發起請求都會被轉到新的地址上。302表示暫時的轉向,比如在服務器端的servlet程序調用了sendRedirect方法,則在客戶端就會得到一個302的代碼,這時服務器返回的頭信息中location的值就是sendRedirect轉向的目標地址。
HttpClient支持自動轉向處理,但是象POST和PUT方式這種要求接受后繼服務的請求方式,暫時不支持自動轉向,因此如果碰到POST方式提交后返回的是301或者302的話需要自己處理。就像剛才在POSTMethod中舉的例子:如果想進入登錄BBS后的頁面,必須重新發起登錄的請求,請求的地址可以在頭字段location中得到。不過需要注意的是,有時候location返回的可能是相對路徑,因此需要對location返回的值做一些處理才可以發起向新地址的請求。
另外除了在頭中包含的信息可能使頁面發生重定向外,在頁面中也有可能會發生頁面的重定向。引起頁面自動轉發的標簽是:<meta http-equiv="refresh" content="5; url=....">。如果你想在程序中也處理這種情況的話得自己分析頁面來實現轉向。需要注意的是,在上面那個標簽中url的值也可以是一個相對地址,如果是這樣的話,需要對它做一些處理后才可以轉發。
處理HTTPS協議
HttpClient提供了對SSL的支持,在使用SSL之前必須安裝JSSE。在Sun提供的1.4以后的版本中,JSSE已經集成到JDK中,如果你使用的是JDK1.4以前的版本則必須安裝JSSE。JSSE不同的廠家有不同的實現。下面介紹怎么使用HttpClient來打開Https連接。這里有兩種方法可以打開https連接,第一種就是得到服務器頒發的證書,然后導入到本地的keystore中;另外一種辦法就是通過擴展HttpClient的類來實現自動接受證書。
方法1,取得證書,並導入本地的keystore:
安裝JSSE (如果你使用的JDK版本是1.4或者1.4以上就可以跳過這一步)。本文以IBM的JSSE為例子說明。先到IBM網站上下載JSSE的安裝包。然后解壓開之后將ibmjsse.jar包拷貝到<java-home>\lib\ext\目錄下。
取得並且導入證書。證書可以通過IE來獲得:
1. 用IE打開需要連接的https網址,會彈出如下對話框:
2. 單擊"View Certificate",在彈出的對話框中選擇"Details",然后再單擊"Copy to File",根據提供的向導生成待訪問網頁的證書文件
3. 向導第一步,歡迎界面,直接單擊"Next",
4. 向導第二步,選擇導出的文件格式,默認,單擊"Next",
5. 向導第三步,輸入導出的文件名,輸入后,單擊"Next",
6. 向導第四步,單擊"Finish",完成向導
7. 最后彈出一個對話框,顯示導出成功
用keytool工具把剛才導出的證書倒入本地keystore。Keytool命令在<java-home>\bin\下,打開命令行窗口,並到<java-home>\lib\security\目錄下,運行下面的命令:
keytool -import -noprompt -keystore cacerts -storepass changeit -alias yourEntry1 -file your.cer
其中參數alias后跟的值是當前證書在keystore中的唯一標識符,但是大小寫不區分;參數file后跟的是剛才通過IE導出的證書所在的路徑和文件名;如果你想刪除剛才導入到keystore的證書,可以用命令:
keytool -delete -keystore cacerts -storepass changeit -alias yourEntry1
寫程序訪問https地址。如果想測試是否能連上https,只需要稍改一下GetSample例子,把請求的目標變成一個https地址。
GetMethod getMethod = new GetMethod("your url");
運行該程序可能出現的問題:
1. 拋出異常java.net.SocketException: Algorithm SSL not available。出現這個異常可能是因為沒有加JSSEProvider,如果用的是IBM的JSSE Provider,在程序中加入這樣的一行:
if(Security.getProvider("com.ibm.jsse.IBMJSSEProvider") == null)
Security.addProvider(new IBMJSSEProvider());
或者也可以打開<java-home>\lib\security\java.security,在行
security.provider.1=sun.security.provider.Sun
security.provider.2=com.ibm.crypto.provider.IBMJCE
后面加入security.provider.3=com.ibm.jsse.IBMJSSEProvider
2. 拋出異常java.net.SocketException: SSL implementation not available。出現這個異常可能是你沒有把ibmjsse.jar拷貝到<java-home>\lib\ext\目錄下。
3. 拋出異常javax.net.ssl.SSLHandshakeException: unknown certificate。出現這個異常表明你的JSSE應該已經安裝正確,但是可能因為你沒有把證書導入到當前運行JRE的keystore中,請按照前面介紹的步驟來導入你的證書。
方法2,擴展HttpClient類實現自動接受證書
因為這種方法自動接收所有證書,因此存在一定的安全問題,所以在使用這種方法前請仔細考慮您的系統的安全需求。具體的步驟如下:
提供一個自定義的socket factory(test.MySecureProtocolSocketFactory)。這個自定義的類必須實現接口org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory,在實現接口的類中調用自定義的X509TrustManager(test.MyX509TrustManager),這兩個類可以在隨本文帶的附件中得到
創建一個org.apache.commons.httpclient.protocol.Protocol的實例,指定協議名稱和默認的端口號 Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory (), 443);
注冊剛才創建的https協議對象 Protocol.registerProtocol("https ", myhttps);
然后按照普通編程方式打開https的目標地址,代碼請參見test.NoCertificationHttpsGetSample
[編輯本段]5 處理代理服務器
HttpClient中使用代理服務器非常簡單,調用HttpClient中setProxy方法就可以,方法的第一個參數是代理服務器地址,第二個參數是端口號。另外HttpClient也支持SOCKS代理。
httpClient.getHostConfiguration().setProxy(hostName,port); 一般的情況下我們都是使用IE或者Navigator瀏覽器來訪問一個WEB服務器,用來瀏覽頁面查看信息或者提交一些數據等等。所訪問的這些頁面有的僅僅是一些普通的頁面,有的需要用戶登錄后方可使用,或者需要認證以及是一些通過加密方式傳輸,例如HTTPS。目前我們使用的瀏覽器處理這些情況都不會構成問題。不過你可能在某些時候需要通過程序來訪問這樣的一些頁面,比如從別人的網頁中“偷”一些數據;利用某些站點提供的頁面來完成某種功能,例如說我們想知道某個手機號碼的歸屬地而我們自己又沒有這樣的數據,因此只好借助其他公司已有的網站來完成這個功能,這個時候我們需要向網頁提交手機號碼並從返回的頁面中解析出我們想要的數據來。如果對方僅僅是一個很簡單的頁面,那我們的程序會很簡單,本文也就沒有必要大張旗鼓的在這里浪費口舌。但是考慮到一些服務授權的問題,很多公司提供的頁面往往並不是可以通過一個簡單的URL就可以訪問的,而必須經過注冊然后登錄后方可使用提供服務的頁面,這個時候就涉及到COOKIE問題的處理。我們知道目前流行的動態網頁技術例如ASP、JSP無不是通過COOKIE來處理會話信息的。為了使我們的程序能使用別人所提供的服務頁面,就要求程序首先登錄后再訪問服務頁面,這過程就需要自行處理cookie,想想當你用java.net.HttpURLConnection來完成這些功能時是多么恐怖的事情啊!況且這僅僅是我們所說的頑固的WEB服務器中的一個很常見的“頑固”!再有如通過HTTP來上傳文件呢?不需要頭疼,這些問題有了“它”就很容易解決了!
我們不可能列舉所有可能的頑固,我們會針對幾種最常見的問題進行處理。當然了,正如前面說到的,如果我們自己使用java.net.HttpURLConnection來搞定這些問題是很恐怖的事情,因此在開始之前我們先要介紹一下一個開放源碼的項目,這個項目就是Apache開源組織中的httpclient,它隸屬於Jakarta的commons項目,目前的版本是2.0RC2。commons下本來已經有一個net的子項目,但是又把httpclient單獨提出來,可見http服務器的訪問絕非易事。
Commons-httpclient項目就是專門設計來簡化HTTP客戶端與服務器進行各種通訊編程。通過它可以讓原來很頭疼的事情現在輕松的解決,例如你不再管是HTTP或者HTTPS的通訊方式,告訴它你想使用HTTPS方式,剩下的事情交給httpclient替你完成。本文會針對我們在編寫HTTP客戶端程序時經常碰到的幾個問題進行分別介紹如何使用httpclient來解決它們,為了讓讀者更快的熟悉這個項目我們最開始先給出一個簡單的例子來讀取一個網頁的內容,然后循序漸進解決掉前進中的所有問題。