`
niedj
  • 浏览: 31035 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

转: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);

具体代码如下:

package org.study.meteor.ticket.util;
 
 import java.io.IOException;    
 import java.net.InetAddress;    
 import java.net.InetSocketAddress;    
 import java.net.Socket;    
 import java.net.SocketAddress;    
 import java.net.UnknownHostException;    
 import java.security.KeyManagementException;    
 import java.security.NoSuchAlgorithmException;    
 import java.security.cert.CertificateException;    
 import java.security.cert.X509Certificate;    
     
 import javax.net.SocketFactory;    
 import javax.net.ssl.SSLContext;    
 import javax.net.ssl.TrustManager;    
 import javax.net.ssl.X509TrustManager;    
     
 import org.apache.commons.httpclient.ConnectTimeoutException;    
 import org.apache.commons.httpclient.params.HttpConnectionParams;    
 import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory; 
 
 /**
  * MySecureProtocolSocketFactory.java.java Create on 2012-9-26下午1:15:03
  * 
  * 
  * Copyright (c) 2012 by MTA.
  * 
  * @author lmeteor
  * @Email txin0814@sina.com
  * @description 自定义的socket factory 实现自动接受证书
  * @version 1.0
  */
 public class MySecureProtocolSocketFactory implements
         SecureProtocolSocketFactory
 {
 
     private SSLContext sslcontext = null;
 
     private SSLContext createSSLContext()
     {
         SSLContext sslcontext = null;
         try
         {
             sslcontext = SSLContext.getInstance("SSL");
             sslcontext.init(null, new TrustManager[]
             { new TrustAnyTrustManager() }, new java.security.SecureRandom());
         }
         catch (NoSuchAlgorithmException e)
         {
             e.printStackTrace();
         }
         catch (KeyManagementException e)
         {
             e.printStackTrace();
         }
         return sslcontext;
     }
 
     private SSLContext getSSLContext()
     {
         if (this.sslcontext == null)
         {
             this.sslcontext = createSSLContext();
         }
         return this.sslcontext;
     }
 
     public Socket createSocket(Socket socket, String host, int port,
             boolean autoClose) throws IOException, UnknownHostException
     {
         return getSSLContext().getSocketFactory().createSocket(socket, host,
                 port, autoClose);
     }
 
     public Socket createSocket(String host, int port) throws IOException,
             UnknownHostException
     {
         return getSSLContext().getSocketFactory().createSocket(host, port);
     }
 
     public Socket createSocket(String host, int port, InetAddress clientHost,
             int clientPort) throws IOException, UnknownHostException
     {
         return getSSLContext().getSocketFactory().createSocket(host, port,
                 clientHost, clientPort);
     }
 
     public Socket createSocket(String host, int port, InetAddress localAddress,
             int localPort, HttpConnectionParams params) throws IOException,
             UnknownHostException, ConnectTimeoutException
     {
         if (params == null)
         {
             throw new IllegalArgumentException("Parameters may not be null");
         }
         int timeout = params.getConnectionTimeout();
         SocketFactory socketfactory = getSSLContext().getSocketFactory();
         if (timeout == 0)
         {
             return socketfactory.createSocket(host, port, localAddress,
                     localPort);
         }
         else
         {
             Socket socket = socketfactory.createSocket();
             SocketAddress localaddr = new InetSocketAddress(localAddress,
                     localPort);
             SocketAddress remoteaddr = new InetSocketAddress(host, port);
             socket.bind(localaddr);
             socket.connect(remoteaddr, timeout);
             return socket;
         }
     }
 
     // 自定义私有类
     private static class TrustAnyTrustManager implements X509TrustManager
     {
 
         public void checkClientTrusted(X509Certificate[] chain, String authType)
                 throws CertificateException
         {
         }
 
         public void checkServerTrusted(X509Certificate[] chain, String authType)
                 throws CertificateException
         {
         }
 
         public X509Certificate[] getAcceptedIssuers()
         {
             return new X509Certificate[]
             {};
         }
     }
 }

 下面的是httpClient的具体实现类:

package org.study.meteor.ticket.util;
 
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.httpclient.HttpClient;
 import org.apache.commons.httpclient.HttpException;
 import org.apache.commons.httpclient.NameValuePair;
 import org.apache.commons.httpclient.methods.PostMethod;
 import org.apache.commons.httpclient.params.HttpMethodParams;
 import org.apache.commons.httpclient.protocol.Protocol;
 
 /**
  * HttpDoPostUtils.java Create on 2012-9-7下午3:08:18
  * 
  * 
  * Copyright (c) 2012 by MTA.
  * 
  * @author lmeteor
  * @Email txin0814@sina.com
  * @description 模拟HTTP发送请求得到报文
  * @version 1.0
  */
 @SuppressWarnings("deprecation")
 public class HttpDoPostUtils
 {
 
     
     private static HttpClient httpClient = null;
     
     static
     {
         //指定协议名称和默认的端口号
         Protocol myhttps = new Protocol("https", new MySecureProtocolSocketFactory(), 443);
         //注册刚才创建的https 协议对象
         Protocol.registerProtocol("https", myhttps); 
         httpClient = new HttpClient();
     }
     
     /**
      * 发送请求报文,得到响应报文
      * @param url
      *             登录请求URL
      * @param pList
      *             是否包含请求参数
      * @return
      * @throws UnsupportedEncodingException
      */
     public static String doRequestToString(String url,List<NameValuePair> pList) throws UnsupportedEncodingException
     {
         //获得postMethod对象
         PostMethod pmethod = getPostMethod(url);
         pmethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8");
         //判断是否包含参数
         if(null != pList && pList.size() > 0)
         {
             pmethod.setRequestBody(pList.toArray(new NameValuePair[pList.size()]));
         }
         String value = "";
         try
         {
             httpClient.executeMethod(pmethod);
             value = pmethod.getResponseBodyAsString();
         }
         catch ( HttpException e )
         {
             e.printStackTrace();
         }
         catch ( IOException e )
         {
             e.printStackTrace();
         }
         
         return value;
     }
     
     /**
      * 获得12306网站的登录验证码
      * @param url
      *             请求URL
      * @param filePath
      *             验证码保存路径 如:e:\\login.jpg
      * @return
      */
     public static File doGetFile(String url,String filePath)
     {
         PostMethod pmethod = getPostMethod(url);
         pmethod.getParams().setParameter(HttpMethodParams.HTTP_CONTENT_CHARSET, "utf-8");
         try
         {
             httpClient.executeMethod(pmethod);
             //得到响应中的流对象
             InputStream in = pmethod.getResponseBodyAsStream();
             //包装 并读出流信息
             BufferedInputStream bis = new BufferedInputStream(in);
             File file = new File(filePath);
             FileOutputStream fs = new FileOutputStream(file);
 
             byte[] buf = new byte[1024];
             int len = bis.read(buf);
             if(len == -1 || len == 0){
                 file.delete();
                 file = null;
             }
             while (len != -1) {
                 fs.write(buf, 0, len);
                 len = bis.read(buf);
             }
             fs.flush();
             fs.close();
             return file;
         }
         catch (HttpException e)
         {
             e.printStackTrace();
         }
         catch (IOException e)
         {
             e.printStackTrace();
         }
         return null;
         
     }
     
     public static List<NameValuePair> createNameValuePair(String params) {
         List<NameValuePair> nvps = new ArrayList<NameValuePair>();
         if (null != params && !params.trim().equals("")) {
             String[] _params = params.split("&");
             // userCookieList = new AttributeList();
             for (int i = 0; i < _params.length; i++) {
                 int _i = _params[i].indexOf("=");
                 if (_i != -1) {
                     String name = _params[i].substring(0, _i);
                     String value = _params[i].substring(_i + 1);
                     nvps.add(new NameValuePair(name, value));
                 }
             }
         }
         return nvps;
     }
     
     
     public static PostMethod getPostMethod(String url)
     {
         PostMethod pmethod = new PostMethod(url);
         //设置响应头信息
         pmethod.addRequestHeader("Connection", "keep-alive");
         pmethod.addRequestHeader("Cache-Control", "max-age=0");
         pmethod.addRequestHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");
         pmethod.addRequestHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
         return pmethod;
     }
     
 }
 

模拟请求的类已经出来,现在开始进行模拟登录,登录前必须知道12306自身是怎样提交请求,并包含哪些参数到后台,通过firebug就很容易找到这些东西了。

12306登录之前会调用“https://dynamic.12306.cn/otsweb/loginAction.do?method=loginAysnSuggest ”,后台返回一串JSON报文,如下:

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

当randError为“Y”时,才对表单FORM提交,并且将loginRand的值初始化到表单里的隐藏域中,作为参数传到后台

现在最后一步就是拼接参数了,具体操作代码如下:

package org.study.meteor.ticket.util;
 
 import java.io.BufferedReader;
 import java.io.InputStreamReader;
 import java.io.UnsupportedEncodingException;
 
 import org.study.meteor.ticket.domain.LoginBeforeValidatior;
 
 import net.sf.json.JSONObject;
 
 /**
  * Login.java.java Create on 2012-9-26下午1:48:42 
  * 
  * 
  * Copyright (c) 2012 by MTA.
  * 
  * @author lmeteor
  * @Email txin0814@sina.com
  * @description 
  * @version 1.0
  */
 public class Login
 {
     
     /**
      * 获取验证码
      * @param filePath
      * @return
      */
     public static String getRandCode(String filePath)
     {
         String randCode = "";
         /** 获取验证码 */
         HttpDoPostUtils.doGetFile(PropertiesUtils.newInstance().getPropertiesValue("loginCode"),filePath);
         randCode = readString("请输入登录验证码:");
         return randCode;
     }
     
 
     /**
      * 实现登录操作
      * @throws UnsupportedEncodingException
      */
     public static void doLogin() throws UnsupportedEncodingException
     {
         String randCode = getRandCode("e:\\login.jpg");
         /** 登录前 提交得到报文 */
         String loginBeforeVal = HttpDoPostUtils.doRequestToString(PropertiesUtils.newInstance().getPropertiesValue("loginBeforeValidatiorUrl"),null);
         //将返回的JSON报文转换成指定的对象
         JSONObject jsonObj = JSONObject.fromObject(loginBeforeVal);
         LoginBeforeValidatior loginBefore = new LoginBeforeValidatior();
         loginBefore = (LoginBeforeValidatior) JSONObject.toBean(jsonObj, LoginBeforeValidatior.class);
         //拼接参数
         StringBuffer params = new StringBuffer();
         params.append("loginRand="+loginBefore.getLoginRand()).append("&")
                 .append("refundLogin=N").append("&")
                 .append("refundFlag=Y").append("&")
                 .append("loginUser.user_name="+PropertiesUtils.newInstance().getPropertiesValue("username")).append("&")
                 .append("nameErrorFocus=&")
                 .append("user.password="+PropertiesUtils.newInstance().getPropertiesValue("password")).append("&")
                 .append("passwordErrorFocus=&")
                 .append("randCode="+randCode).append("&")
                 .append("randErrorFocus=");
         //像服务器发送登录请求 并返回对应的报文
         String loginResponseText = HttpDoPostUtils.doRequestToString(PropertiesUtils.newInstance().getPropertiesValue("loginUrl"),HttpDoPostUtils.createNameValuePair(params.toString()));
         System.out.println(loginResponseText);
         
     }
     
     
     /**
      * 多控制台读取验证码
      * @param msg
      * @return
      * @throws Exception
      */
     private static String readString(String msg)
     {
         BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
         try{
             System.out.print(msg+": ");
             return bufferedReader.readLine();
         }catch(Exception e){
         }
         return "1245";
     }
     
     public static void main(String[] args) throws UnsupportedEncodingException
     {
         //Login login = new Login();
         //login.doLogin();
         Login.doLogin();
     }
 }

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

#12306登录之前调用URL
 loginBeforeValidatiorUrl=https://dynamic.12306.cn/otsweb/loginAction.do?method=loginAysnSuggest
 #12306登录验证码的地址
 loginCode=https://dynamic.12306.cn/otsweb/passCodeAction.do?rand=sjrand
 #登录URL
 loginUrl=https://dynamic.12306.cn/otsweb/loginAction.do?method=login
 #用户名
 username=xxxx
 #密码
 password=xxx

 通过返回的HTML,如果看到自己的名字,就说明登录成功了。如果大家还想做什么动作,就可以发挥大家的想像力了。

只作为学习使用!

转自:http://www.cnblogs.com/lmeteor/archive/2012/09/27/2705458.html

分享到:
评论

相关推荐

    模拟12306铁路购票系统

    在信息技术日益发达的今天,模拟12306铁路购票系统是一项具有挑战性的实践项目,它涉及到多种核心技术的运用,如用户登录注册、购票流程、信息安全管理以及支付功能等。本篇文章将深入探讨这个项目中的关键知识点,...

    基于java httpclient的12306 买票软件, 仅供学习使用.zip

    通过以上内容,我们可以了解到Java HttpClient在实现12306购票软件中的关键作用,包括如何进行网络请求、处理响应、以及应对12306网站的特有挑战。学习并掌握这些知识点,不仅有助于开发购票软件,还能为其他需要与...

    12306购票辅助工具

    总结来说,"12306购票辅助工具"是一个综合运用了C#编程、验证码识别技术、网页数据解析和自动登录策略的软件项目,旨在提高用户在12306购票的效率和便捷性。它的开发涉及到的知识点广泛,既包括基础的编程语言和框架...

    12306订票助手

    - **官方购票优先**:尽管12306订票助手能提高购票效率,但官方12306网站仍是最安全的购票渠道。 - **网络安全**:确保使用安全的网络环境,避免账号信息被盗。 - **软件来源**:下载软件应选择正规渠道,防止...

    C#12306一键订票源码

    该项目虽然未完成,但其开放源码的特性为开发者提供了一个学习和研究12306购票系统的平台,有助于提升对网络爬虫、自动化脚本以及数据处理的理解。 首先,从“winFormYZM”这个文件名来看,我们可以推断这是项目中...

    12306余票查询系统C#

    12306是中国铁路官方售票平台,为旅客提供购票、退票、改签等服务。由于其庞大的用户基数和实时性需求,12306的余票查询系统需要具备高效稳定、数据准确的特点。 C#是一种由微软公司推出的面向对象的编程语言,广泛...

    用httpclient开发的在线自动抢订火车票系统

    在线自动抢订火车票系统是基于HTTP协议的网络应用程序,主要利用了Apache HttpClient库来实现对火车票购票网站的模拟访问和数据提交。HttpClient是一个强大的Java客户端HTTP编程库,它提供了一种优雅的方式处理HTTP...

    httpclient自动购买火车票

    基于“httpclient”和“火车票”的标签,我们可以推测这个程序可能是通过模拟浏览器行为,发送HTTP请求到火车票购票网站,如12306,来完成购票流程。HttpClient库可以用来设置HTTP头、POST数据、处理cookies等,模拟...

    12306-ticket-master

    10. **模拟登录与验证码识别**:为了成功登录12306账号,软件可能需要模拟浏览器行为,包括处理Cookie和Session。此外,对于复杂的图形验证码,可能需要结合OCR(光学字符识别)技术进行识别。 11. **测试与调试**...

    12306抢票工具--抢票C# Demo

    ”暗示了该工具的工作原理可能涉及到定时刷新、自动填表、快速提交订单等技术,以应对12306网站的高并发环境和瞬时购票需求。同时,“希望对你有所帮助,能扩展一些”提示我们这个Demo不仅是一个完整的应用,还可能...

    gitee下载的12306抢票软件

    2. **HTTP请求库**:Java中如`HttpURLConnection`、`Apache HttpClient`或`OkHttp`等库用于发送HTTP请求到12306网站,模拟用户登录、查询余票和提交订单等操作。 3. **网页抓取**:软件可能使用`Jsoup`等库解析...

    .NET 12306订票助手

    《.NET 12306订票助手详解》 在现代互联网技术中,.NET框架作为微软开发的一款强大工具,被广泛应用于各种应用程序的...开发者通过巧妙运用这些技术,实现了与12306网站的高效交互,为用户提供了一种便捷的购票方式。

Global site tag (gtag.js) - Google Analytics