Apache HttpClient類比登入

來源:互聯網
上載者:User
Apache HttpClient類比登入    HttpClient 是 Apache Jakarta Common 下的子項目,可以用來提供高效的、最新的、功能豐富的支援 HTTP 協議的用戶端編程工具包,並且它支援 HTTP 協議最新的版本和建議。

HttpClient 功能介紹

以下列出的是 HttpClient 提供的主要的功能,要知道更多詳細的功能可以參見 HttpClient 的首頁。

  • 實現了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)
  • 支援自動轉向
  • 支援 HTTPS 協議
  • 支援Proxy 伺服器等

下面將逐一介紹怎樣使用這些功能。首先,我們必須安裝好 HttpClient。

  • HttpClient 可以在http://jakarta.apache.org/commons/httpclient/downloads.html下載
  • HttpClient 用到了 Apache Jakarta common 下的子項目 logging,你可以從這個地址http://jakarta.apache.org/site/downloads/downloads_commons-logging.cgi下載到 common logging,從下載後的壓縮包中取出 commons-logging.jar 加到 CLASSPATH 中
  • HttpClient 用到了 Apache Jakarta common 下的子項目 codec,你可以從這個地址http://jakarta.apache.org/site/downloads/downloads_commons-codec.cgi 下載到最新的 common codec,從下載後的壓縮包中取出 commons-codec-1.x.jar 加到 CLASSPATH 中

下面是個我寫的程式:package com.newpalm.unicomfetch.threads;import java.util.Vector;import org.apache.commons.httpclient.Cookie;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.cookie.CookiePolicy;
import org.apache.commons.httpclient.cookie.CookieSpec;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;
import org.htmlparser.Node;
import org.htmlparser.Parser;
import org.htmlparser.tags.TableColumn;
import org.htmlparser.tags.TableRow;
import org.htmlparser.tags.TableTag;
import org.htmlparser.visitors.ObjectFindingVisitor;
/**
 * 用來示範登入表單的樣本
 * @author Liudong
 */
public class FormLoginDemo {
   
    public static void main(String[] args) throws Exception{
     Parser parser = null;
     ObjectFindingVisitor visitor = null;
        HttpClient client = new HttpClient();
       
        //類比登入頁面
        PostMethod post = new PostMethod("http://211.90.119.58:9999/SPLogin.aspx");
        NameValuePair name = new NameValuePair("txtHandsetNumber", "123456");    
        NameValuePair pass = new NameValuePair("txtPassword", "123456");
        NameValuePair __VIEWSTATE = new NameValuePair("__VIEWSTATE", "dDwtMTUxMzkxNTQ0Mzt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjs+O2w8dDxwPGw8VGV4dDs+O2w8XDxtYXJxdWVlIERpcmVjdGlvbj0idXAiIGJlaGF2aW9yPSJzY3JvbGwiICBoZWlnaHQ9JzE1MCcgc2Nyb2xsZGVsYXk9MzAwIG9ubW91c2VvdmVyPSd0aGlzLnN0b3AoKScgb25tb3VzZW91dD0ndGhpcy5zdGFydCgpJ1w+XDxpbWcgc3JjPSJpbWFnZS9kZWZhdWx0X2NhbGxfaWNvbi5naWYiIHdpZHRoPSI0IiBoZWlnaHQ9IjciXD4mbmJzcFw7Jm5ic3BcO1w8YSBocmVmID0iIyIgb25jbGljaz0ib3BlbldpbmRvdygnL0J1bGxldGluLmFzcHg/QnVsbGV0aW5JZD0yMycpIlw+c3DkuJrliqHmjqXlhaXms6jmhI/kuovpoblcPC9hXD4KXDxiclw+XDxiclw+XDwvbWFycXVlZVw+Oz4+Ozs+Oz4+Oz4+O2w8YnRuTG9naW47Pj4pKEgdIvj1JmRdgJaS7sGzD39jZg==");
        NameValuePair btnLoginx = new NameValuePair("btnLogin.x", "0");
        NameValuePair btnLoginy = new NameValuePair("btnLogin.y", "5");
        post.setRequestBody(new NameValuePair[]{name,pass,__VIEWSTATE,btnLoginx,btnLoginy});
        int status = client.executeMethod(post);
       
        post.releaseConnection(); 
        //查看cookie資訊
        CookieSpec cookiespec = CookiePolicy.getDefaultSpec();
        Cookie[] cookies = client.getState().getCookies();
        if (cookies.length == 0) {
           System.out.println("None");   
        } else {
           for (int i = 0; i < cookies.length; i++) {
              System.out.println(cookies[i].toString());   
           }
       }
       
       //訪問所需的頁面
        GetMethod get = new GetMethod("http://211.90.119.58:9999/PhoneSearch/PhoneSearch.aspx");
        client.executeMethod(get);
        visitor = new ObjectFindingVisitor(TableTag.class);
  parser = new Parser();
  parser.setInputHTML(get.getResponseBodyAsString());
  parser.setEncoding("GBK");
  parser.visitAllNodesWith(visitor);
  
  //取得要解析的頁面數
  Node[] tables = visitor.getTags();
  TableTag tableTag = (TableTag) tables[tables.length-1];
  TableRow[] rows = tableTag.getRows();
  TableRow row = rows[0];
  TableColumn[] col = row.getColumns();
  int pageNumber = Integer.parseInt(col[0].getChildrenHTML().substring(25, 29));
  get.releaseConnection();
  
  for(int i=1;i<pageNumber;i++){
   PostMethod pt = new PostMethod("http://211.90.119.58:9999/PhoneSearch/PhoneSearch.aspx");   
   NameValuePair txtPage = new NameValuePair("txtPage",Integer.toString(i));
         __VIEWSTATE = new NameValuePair("__VIEWSTATE", "")
         NameValuePair __EVENTTARGET = new NameValuePair("__EVENTTARGET", "");
         NameValuePair __EVENTARGUMENT = new NameValuePair("__EVENTARGUMENT", "");
         NameValuePair TBMDN = new NameValuePair("TBMDN", "");
         NameValuePair TBServiceType = new NameValuePair("TBServiceType", "");
         NameValuePair TBStartTime = new NameValuePair("TBStartTime", "");
         NameValuePair TBEndTime = new NameValuePair("TBEndTime", "");
         NameValuePair btnGotox = new NameValuePair("btnGoto.x", "26");
         NameValuePair btnGotoy = new NameValuePair("btnGoto.y", "13");
         pt.setRequestBody(new NameValuePair[]{__EVENTTARGET,__EVENTARGUMENT,__VIEWSTATE,TBMDN,TBServiceType,TBStartTime,TBEndTime,txtPage,btnGotox,btnGotoy});
         int a = client.executeMethod(pt);
        
        
         parser.setInputHTML(pt.getResponseBodyAsString());
   parser.setEncoding("GBK");
   parser.visitAllNodesWith(visitor);
   
   tables = visitor.getTags();
   tableTag = (TableTag) tables[tables.length-3];
   
   rows = tableTag.getRows();
   row = rows[1];
   col = row.getColumns();
   System.out.println(col[4].getChildrenHTML().toString());
         get.releaseConnection();
  }
       
    }

使用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=http://www.ibm.com/us">。如果你想在程式中也處理這種情況的話得自己分析頁面來實現轉向。需要注意的是,在上面那個標籤中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("https://www.yourdomain.com");

    運行該程式可能出現的問題:

    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.Sunsecurity.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

處理Proxy 伺服器

HttpClient中使用Proxy 伺服器非常簡單,調用HttpClient中setProxy方法就可以,方法的第一個參數是Proxy 伺服器地址,第二個參數是連接埠號碼。另外HttpClient也支援SOCKS代理。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.