HttpClient模擬登錄12306購票網站


  首先12306網站前綴為“https://” 表明是用SSL加密。

  用HttpClient去模擬發送請求時,對於URL用為“https”時,先要解決證書問題,有兩種解決方案:

   a.使證書被信任。

     在查找相關資料時,對於這種方法有點麻煩,最后就沒有去嘗試,有興趣的朋友可以試試。

        b.使用httpClient時不檢測服務器證書是否可信

    擴展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);

具體代碼如下:

  1 package org.study.meteor.ticket.util;
  2 
  3 import java.io.IOException;    
  4 import java.net.InetAddress;    
  5 import java.net.InetSocketAddress;    
  6 import java.net.Socket;    
  7 import java.net.SocketAddress;    
  8 import java.net.UnknownHostException;    
  9 import java.security.KeyManagementException;    
 10 import java.security.NoSuchAlgorithmException;    
 11 import java.security.cert.CertificateException;    
 12 import java.security.cert.X509Certificate;    
 13     
 14 import javax.net.SocketFactory;    
 15 import javax.net.ssl.SSLContext;    
 16 import javax.net.ssl.TrustManager;    
 17 import javax.net.ssl.X509TrustManager;    
 18     
 19 import org.apache.commons.httpclient.ConnectTimeoutException;    
 20 import org.apache.commons.httpclient.params.HttpConnectionParams;    
 21 import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; 
 22 
 23 /**
 24  * MySecureProtocolSocketFactory.java.java Create on 2012-9-26下午1:15:03
 25  * 
 26  * 
 27  * Copyright (c) 2012 by MTA.
 28  * 
 29  * @author lmeteor
 30  * @Email txin0814@sina.com
 31  * @description 自定義的socket factory 實現自動接受證書
 32  * @version 1.0
 33  */
 34 public class MySecureProtocolSocketFactory implements
 35         SecureProtocolSocketFactory
 36 {
 37 
 38     private SSLContext sslcontext = null;
 39 
 40     private SSLContext createSSLContext()
 41     {
 42         SSLContext sslcontext = null;
 43         try
 44         {
 45             sslcontext = SSLContext.getInstance("SSL");
 46             sslcontext.init(null, new TrustManager[]
 47             { new TrustAnyTrustManager() }, new java.security.SecureRandom());
 48         }
 49         catch (NoSuchAlgorithmException e)
 50         {
 51             e.printStackTrace();
 52         }
 53         catch (KeyManagementException e)
 54         {
 55             e.printStackTrace();
 56         }
 57         return sslcontext;
 58     }
 59 
 60     private SSLContext getSSLContext()
 61     {
 62         if (this.sslcontext == null)
 63         {
 64             this.sslcontext = createSSLContext();
 65         }
 66         return this.sslcontext;
 67     }
 68 
 69     public Socket createSocket(Socket socket, String host, int port,
 70             boolean autoClose) throws IOException, UnknownHostException
 71     {
 72         return getSSLContext().getSocketFactory().createSocket(socket, host,
 73                 port, autoClose);
 74     }
 75 
 76     public Socket createSocket(String host, int port) throws IOException,
 77             UnknownHostException
 78     {
 79         return getSSLContext().getSocketFactory().createSocket(host, port);
 80     }
 81 
 82     public Socket createSocket(String host, int port, InetAddress clientHost,
 83             int clientPort) throws IOException, UnknownHostException
 84     {
 85         return getSSLContext().getSocketFactory().createSocket(host, port,
 86                 clientHost, clientPort);
 87     }
 88 
 89     public Socket createSocket(String host, int port, InetAddress localAddress,
 90             int localPort, HttpConnectionParams params) throws IOException,
 91             UnknownHostException, ConnectTimeoutException
 92     {
 93         if (params == null)
 94         {
 95             throw new IllegalArgumentException("Parameters may not be null");
 96         }
 97         int timeout = params.getConnectionTimeout();
 98         SocketFactory socketfactory = getSSLContext().getSocketFactory();
 99         if (timeout == 0)
100         {
101             return socketfactory.createSocket(host, port, localAddress,
102                     localPort);
103         }
104         else
105         {
106             Socket socket = socketfactory.createSocket();
107             SocketAddress localaddr = new InetSocketAddress(localAddress,
108                     localPort);
109             SocketAddress remoteaddr = new InetSocketAddress(host, port);
110             socket.bind(localaddr);
111             socket.connect(remoteaddr, timeout);
112             return socket;
113         }
114     }
115 
116     // 自定義私有類
117     private static class TrustAnyTrustManager implements X509TrustManager
118     {
119 
120         public void checkClientTrusted(X509Certificate[] chain, String authType)
121                 throws CertificateException
122         {
123         }
124 
125         public void checkServerTrusted(X509Certificate[] chain, String authType)
126                 throws CertificateException
127         {
128         }
129 
130         public X509Certificate[] getAcceptedIssuers()
131         {
132             return new X509Certificate[]
133             {};
134         }
135     }
136 }

下面的是httpClient的具體實現類:

  1 package org.study.meteor.ticket.util;
  2 
  3 import java.io.BufferedInputStream;
  4 import java.io.File;
  5 import java.io.FileOutputStream;
  6 import java.io.IOException;
  7 import java.io.InputStream;
  8 import java.io.UnsupportedEncodingException;
  9 import java.util.ArrayList;
 10 import java.util.List;
 11 
 12 import org.apache.commons.httpclient.HttpClient;
 13 import org.apache.commons.httpclient.HttpException;
 14 import org.apache.commons.httpclient.NameValuePair;
 15 import org.apache.commons.httpclient.methods.PostMethod;
 16 import org.apache.commons.httpclient.params.HttpMethodParams;
 17 import org.apache.commons.httpclient.protocol.Protocol;
 18 
 19 /**
 20  * HttpDoPostUtils.java Create on 2012-9-7下午3:08:18
 21  * 
 22  * 
 23  * Copyright (c) 2012 by MTA.
 24  * 
 25  * @author lmeteor
 26  * @Email txin0814@sina.com
 27  * @description 模擬HTTP發送請求得到報文
 28  * @version 1.0
 29  */
 30 @SuppressWarnings("deprecation")
 31 public class HttpDoPostUtils
 32 {
 33 
 34     
 35     private static HttpClient httpClient = null;
 36     
 37     static
 38     {
 39         //指定協議名稱和默認的端口號
 40         Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory(), 443);
 41         //注冊剛才創建的https 協議對象
 42         Protocol.registerProtocol("https", myhttps); 
 43         httpClient = new HttpClient();
 44     }
 45     
 46     /**
 47      * 發送請求報文,得到響應報文
 48      * @param url
 49      *             登錄請求URL
 50      * @param pList
 51      *             是否包含請求參數
 52      * @return
 53      * @throws UnsupportedEncodingException
 54      */
 55     public static String doRequestToString(String url,List<NameValuePair> pList) throws UnsupportedEncodingException
 56     {
 57         //獲得postMethod對象
 58         PostMethod pmethod = getPostMethod(url);
 59         pmethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8");
 60         //判斷是否包含參數
 61         if(null != pList && pList.size() > 0)
 62         {
 63             pmethod.setRequestBody(pList.toArray(new NameValuePair[pList.size()]));
 64         }
 65         String value = "";
 66         try
 67         {
 68             httpClient.executeMethod(pmethod);
 69             value = pmethod.getResponseBodyAsString();
 70         }
 71         catch ( HttpException e )
 72         {
 73             e.printStackTrace();
 74         }
 75         catch ( IOException e )
 76         {
 77             e.printStackTrace();
 78         }
 79         
 80         return value;
 81     }
 82     
 83     /**
 84      * 獲得12306網站的登錄驗證碼
 85      * @param url
 86      *             請求URL
 87      * @param filePath
 88      *             驗證碼保存路徑 如:e:\\login.jpg
 89      * @return
 90      */
 91     public static File doGetFile(String url,String filePath)
 92     {
 93         PostMethod pmethod = getPostMethod(url);
 94         pmethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8");
 95         try
 96         {
 97             httpClient.executeMethod(pmethod);
 98             //得到響應中的流對象
 99             InputStream in = pmethod.getResponseBodyAsStream();
100             //包裝 並讀出流信息
101             BufferedInputStream bis = new BufferedInputStream(in);
102             File file = new File(filePath);
103             FileOutputStream fs = new FileOutputStream(file);
104 
105             byte[] buf = new byte[1024];
106             int len = bis.read(buf);
107             if(len == -1 || len == 0){
108                 file.delete();
109                 file = null;
110             }
111             while (len != -1) {
112                 fs.write(buf, 0, len);
113                 len = bis.read(buf);
114             }
115             fs.flush();
116             fs.close();
117             return file;
118         }
119         catch (HttpException e)
120         {
121             e.printStackTrace();
122         }
123         catch (IOException e)
124         {
125             e.printStackTrace();
126         }
127         return null;
128         
129     }
130     
131     public static List<NameValuePair> createNameValuePair(String params) {
132         List<NameValuePair> nvps = new ArrayList<NameValuePair>();
133         if (null != params && !params.trim().equals("")) {
134             String[] _params = params.split("&");
135             // userCookieList = new AttributeList();
136             for (int i = 0; i < _params.length; i++) {
137                 int _i = _params[i].indexOf("=");
138                 if (_i != -1) {
139                     String name = _params[i].substring(0, _i);
140                     String value = _params[i].substring(_i + 1);
141                     nvps.add(new NameValuePair(name, value));
142                 }
143             }
144         }
145         return nvps;
146     }
147     
148     
149     public static PostMethod getPostMethod(String url)
150     {
151         PostMethod pmethod = new PostMethod(url);
152         //設置響應頭信息
153         pmethod.addRequestHeader("Connection", "keep-alive");
154         pmethod.addRequestHeader("Cache-Control", "max-age=0");
155         pmethod.addRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
156         pmethod.addRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
157         return pmethod;
158     }
159     
160 }

模擬請求的類已經出來,現在開始進行模擬登錄,登錄前必須知道12306自身是怎樣提交請求,並包含哪些參數到后台,通過firebug就很容易找到這些東西了。

12306登錄之前會調用“https://dynamic.12306.cn/otsweb/loginAction.do?method=loginAysnSuggest”,后台返回一串JSON報文,如下:

1 {"loginRand":"652","randError":"Y"}

當randError為“Y”時,才對表單FORM提交,並且將loginRand的值初始化到表單里的隱藏域中,作為參數傳到后台

現在最后一步就是拼接參數了,具體操作代碼如下:

 1 package org.study.meteor.ticket.util;
 2 
 3 import java.io.BufferedReader;
 4 import java.io.InputStreamReader;
 5 import java.io.UnsupportedEncodingException;
 6 
 7 import org.study.meteor.ticket.domain.LoginBeforeValidatior;
 8 
 9 import net.sf.json.JSONObject;
10 
11 /**
12  * Login.java.java Create on 2012-9-26下午1:48:42 
13  * 
14  * 
15  * Copyright (c) 2012 by MTA.
16  * 
17  * @author lmeteor
18  * @Email txin0814@sina.com
19  * @description 
20  * @version 1.0
21  */
22 public class Login
23 {
24     
25     /**
26      * 獲取驗證碼
27      * @param filePath
28      * @return
29      */
30     public static String getRandCode(String filePath)
31     {
32         String randCode = "";
33         /** 獲取驗證碼 */
34         HttpDoPostUtils.doGetFile(PropertiesUtils.newInstance().getPropertiesValue("loginCode"),filePath);
35         randCode = readString("請輸入登錄驗證碼:");
36         return randCode;
37     }
38     
39 
40     /**
41      * 實現登錄操作
42      * @throws UnsupportedEncodingException
43      */
44     public static void doLogin() throws UnsupportedEncodingException
45     {
46         String randCode = getRandCode("e:\\login.jpg");
47         /** 登錄前 提交得到報文 */
48         String loginBeforeVal = HttpDoPostUtils.doRequestToString(PropertiesUtils.newInstance().getPropertiesValue("loginBeforeValidatiorUrl"),null);
49         //將返回的JSON報文轉換成指定的對象
50         JSONObject jsonObj = JSONObject.fromObject(loginBeforeVal);
51         LoginBeforeValidatior loginBefore = new LoginBeforeValidatior();
52         loginBefore = (LoginBeforeValidatior) JSONObject.toBean(jsonObj, LoginBeforeValidatior.class);
53         //拼接參數
54         StringBuffer params = new StringBuffer();
55         params.append("loginRand="+loginBefore.getLoginRand()).append("&")
56                 .append("refundLogin=N").append("&")
57                 .append("refundFlag=Y").append("&")
58                 .append("loginUser.user_name="+PropertiesUtils.newInstance().getPropertiesValue("username")).append("&")
59                 .append("nameErrorFocus=&")
60                 .append("user.password="+PropertiesUtils.newInstance().getPropertiesValue("password")).append("&")
61                 .append("passwordErrorFocus=&")
62                 .append("randCode="+randCode).append("&")
63                 .append("randErrorFocus=");
64         //像服務器發送登錄請求 並返回對應的報文
65         String loginResponseText = HttpDoPostUtils.doRequestToString(PropertiesUtils.newInstance().getPropertiesValue("loginUrl"),HttpDoPostUtils.createNameValuePair(params.toString()));
66         System.out.println(loginResponseText);
67         
68     }
69     
70     
71     /**
72      * 多控制台讀取驗證碼
73      * @param msg
74      * @return
75      * @throws Exception
76      */
77     private static String readString(String msg)
78     {
79         BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
80         try{
81             System.out.print(msg+": ");
82             return bufferedReader.readLine();
83         }catch(Exception e){
84         }
85         return "1245";
86     }
87     
88     public static void main(String[] args) throws UnsupportedEncodingException
89     {
90         //Login login = new Login();
91         //login.doLogin();
92         Login.doLogin();
93     }
94 }

URL都是在配置文件中的,大致如下:

 1 #12306登錄之前調用URL
 2 loginBeforeValidatiorUrl=https://dynamic.12306.cn/otsweb/loginAction.do?method=loginAysnSuggest
 3 #12306登錄驗證碼的地址
 4 loginCode=https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=sjrand
 5 #登錄URL
 6 loginUrl=https://dynamic.12306.cn/otsweb/loginAction.do?method=login
 7 #用戶名
 8 username=xxxx
 9 #密碼
10 password=xxx

通過返回的HTML,如果看到自己的名字,就說明登錄成功了。如果大家還想做什么動作,就可以發揮大家的想像力了。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM