01.使用httpclient4 操作 12306 之 登录篇


注意:本文仅限学习交流,使用本文内容做任何违法的事情本人概不负责。

本人原创,转载请注明出处  http://blog.csdn.net/wang_situ/article/details/42262857


          每年的这个时候,到了抢票季,在外面摸爬滚打了一年,终于有了时间回家一趟,可惜一票难求。

于是各种抢票软件(含流氓推广)、各种黄牛层出不穷。 加上最近12306在打击第三方抢票软件,使得买上

一张小小的火车票越来越难了。

        在下不才,工作闲暇时间对12306进行了研究,收集了各种资料,决定用java写一个抢票软件。

不求比其他抢票软件好上多少,只求分享后,各路大神过来指点一二,一起完善。


-----------------------------------------------华丽的分割线----------------------------------------------------------

世上无难事,只要肯攀登


首先我们来看看现在12306的登录. 以及如何判断第三方软件。

工程截图 

01.使用httpclient4 操作 12306 之 登录篇_第1张图片

运行效果截图

01.使用httpclient4 操作 12306 之 登录篇_第2张图片

01.使用httpclient4 操作 12306 之 登录篇_第3张图片

Http 请求头分析

01.使用httpclient4 操作 12306 之 登录篇_第4张图片


好了,现在开始贴代码了

1. https ssl处理类 (这个网上很多)

package cn.wangsitu.util.ssl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;

import org.apache.commons.httpclient.ConnectTimeoutException;
import org.apache.commons.httpclient.HttpClientError;
import org.apache.commons.httpclient.params.HttpConnectionParams;
import org.apache.commons.httpclient.protocol.ControllerThreadSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;

public class MySecureProtocolSocketFactory implements
		SecureProtocolSocketFactory {

	private SSLContext sslContext = null;

	public MySecureProtocolSocketFactory() {
	}

	private static SSLContext createEasySSLContext() {
		try {
			SSLContext context = SSLContext.getInstance("SSL");
			context.init(null, new TrustManager[] { new MyX509TrustManager() },null);
			return context;
		} catch (Exception e) {
			throw new HttpClientError(e.toString());
		}
	}

	/**
	 * 
	 * @return
	 */
	private SSLContext getSSLContext() {
		if (this.sslContext == null) {
			this.sslContext = createEasySSLContext();
		}
		return this.sslContext;
	}

	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(final String host, final int port,
			final InetAddress localAddress, final int localPort,
			final HttpConnectionParams params) throws IOException,
			UnknownHostException, ConnectTimeoutException {
		if (params == null) {
			throw new IllegalArgumentException("Parameters may not be null");
		}
		int timeout = params.getConnectionTimeout();
		if (timeout == 0) {
			return createSocket(host, port, localAddress, localPort);
		} else {
			return ControllerThreadSocketFactory.createSocket(this, host, port,localAddress, localPort, timeout);
		}
	}

	public Socket createSocket(String host, int port) throws IOException,
			UnknownHostException {
		return getSSLContext().getSocketFactory().createSocket(host, port);
	}

	public Socket createSocket(Socket socket, String host, int port,
			boolean autoClose) throws IOException, UnknownHostException {
		return getSSLContext().getSocketFactory().createSocket(socket, host,port, autoClose);
	}
}


package cn.wangsitu.util.ssl;

import java.security.cert.CertificateException;  
import java.security.cert.X509Certificate;  
  
import javax.net.ssl.X509TrustManager;  
  
public class MyX509TrustManager implements X509TrustManager {  
  
    /* (non-Javadoc) 
     * @see javax.net.ssl.X509TrustManager#checkClientTrusted(java.security.cert.X509Certificate[], java.lang.String) 
     */  
    public void checkClientTrusted(X509Certificate[] arg0, String arg1)  
        throws CertificateException {  
  
    }  
  
    /* (non-Javadoc) 
     * @see javax.net.ssl.X509TrustManager#checkServerTrusted(java.security.cert.X509Certificate[], java.lang.String) 
     */  
    public void checkServerTrusted(X509Certificate[] arg0, String arg1)  
        throws CertificateException {  
  
    }  
  
    /* (non-Javadoc) 
     * @see javax.net.ssl.X509TrustManager#getAcceptedIssuers() 
     */  
    public X509Certificate[] getAcceptedIssuers() {  
        return null;  
    }  
  
}  


下面是核心登录

package cn.wangsitu.main;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import cn.wangsitu.util.ssl.MySecureProtocolSocketFactory;
import cn.wangsitu.util.ssl.MyX509TrustManager;

/**
 * 12306 登录例子
 * 
 * @author wang.situ
 * @blog http://blog.csdn.net/wang_situ
 * @date 2014-12-30
 */
public class LoginApplication {
	
	/*  定义静态变量接收 登录成功的数据 后续[买票时]会用到  */
	private static String cookieString;
	private static String login_key;
	private static String login_value;
	
	public static void main(String[] args) throws Exception {
		login("你的帐号", "你的密码");
	}

	
	/**
	 * 获取 支持 ssl的ThreadSafeClientConnmanager
	 * 12306用的是 https的协议,所以要做修改
	 */
	public static ThreadSafeClientConnManager getClientManager() throws Exception {
		SSLContext ctx = SSLContext.getInstance("TLS");
        X509TrustManager tm = new MyX509TrustManager();
        
        ctx.init(null, new TrustManager[] { tm }, null);
        SSLSocketFactory ssf = new SSLSocketFactory(ctx, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
        SchemeRegistry registry = new SchemeRegistry();
        registry.register(new Scheme("https", 443, ssf));
        ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(registry);
        
        return mgr;
	}
	
	/**
	 * 登录逻辑
	 * @param username 明文帐号
	 * @param password 明文密码
	 **/
	public static DefaultHttpClient login(String username,String password) throws Exception {
		
		String url = "https://kyfw.12306.cn/otn/login/init";
		
		ProtocolSocketFactory fcty = new MySecureProtocolSocketFactory();  
		Protocol.registerProtocol("https", new Protocol("https", fcty, 443));  
		/*初始cookie*/
		String cookie = "_jc_save_detail=true; _jc_save_showZtkyts=true;";
        DefaultHttpClient client = new DefaultHttpClient(getClientManager());
		HttpGet get = new HttpGet(url);
		get.addHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8");
		get.addHeader("Accept-Language", "zh-CN,zh;q=0.8");
		get.addHeader("Connection", "Keep-Alive");
		get.addHeader("Cookie",cookie);
		get.addHeader("Host", "kyfw.12306.cn");
		get.addHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36");
		HttpResponse response = client.execute(get);
		
		String responseText = EntityUtils.toString(response.getEntity(), "GBK");
		
		get.abort();
		List list = client.getCookieStore().getCookies();
		
		for(Cookie c : list){
			cookie += c.getName()+"="+c.getValue()+";";
		}
		System.out.println("HttpHeader Cookie:   "+cookie);
		
		/* ------ 以上逻辑主要是获取cookie   JSESSIONID 值 */
		
		
		/* ------ https://kyfw.12306.cn/otn/login/init 返回的数据会动态的引入一个js  这里主要是截取 
		 * 这个js很重要,主要是动态生成一个登录的key和value,没有的话,你的就是第三方非法软件
		 * 
		 * */
		int rindex = responseText.indexOf("otn/dynamicJs");
		String jsurl = "";
		String kkky = "";
		String vvvy = "1111";
		if(rindex!=-1){
			
			String rtxt = responseText.substring(rindex,responseText.length());
			int r33 = rtxt.indexOf("\"");
			jsurl = "https://kyfw.12306.cn/"+rtxt.substring(0, r33);
			HttpGet jsget = new HttpGet(jsurl);
			jsget.addHeader("Accept","*/*");
			jsget.addHeader("Connection","keep-alive");
			jsget.addHeader("Cookie",cookie);
			jsget.addHeader("Host","kyfw.12306.cn");
			jsget.addHeader("Referer","https://kyfw.12306.cn/otn/login/init");
			jsget.addHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36");
			
			HttpResponse strRes = client.execute(jsget);
			
			
			String strScr = EntityUtils.toString(strRes.getEntity(), "GBK");
			int ri1 = strScr.indexOf("var key='");
			strScr = strScr.substring(ri1,strScr.length());
			int ri2 = strScr.indexOf("';");
			kkky = strScr.substring(9,ri2);
			jsget.abort();
			ScriptEngineManager manager = new ScriptEngineManager();
			ScriptEngine engine = manager.getEngineByName("javascript");
			String mygc = "function kkv(){return encode32(bin216(Base32.encrypt('"+vvvy+"', '"+kkky+"')));}function bin216(s){var i,l,o=\"\",n;s+=\"\";b=\"\";for(i=0,l=s.length;in))return null;n=m}for(var i=0;i>>8&0xff,data[i]>>>16&0xff,data[i]>>>24&0xff)}if(includeLength){return data.join('').substring(0,n)}else{return data.join('')}};function stringToLongArray(string,includeLength){var length=string.length;var result=[];for(var i=0;i>2]=string.charCodeAt(i)|string.charCodeAt(i+1)<<8|string.charCodeAt(i+2)<<16|string.charCodeAt(i+3)<<24}if(includeLength){result[result.length]=length}return result};this.encrypt=function(string,key){if(string==\"\"){return\"\"}var v=stringToLongArray(string,true);var k=stringToLongArray(key,false);if(k.length<4){k.length=4}var n=v.length-1;var z=v[n],y=v[0];var mx,e,p,q=Math.floor(6+52/(n+1)),sum=0;while(0>>2&3;for(p=0;p>>5^y<<2)+(y>>>3^z<<4)^(sum^y)+(k[p&3^e]^z);z=v[p]=v[p]+mx&0xffffffff}y=v[0];mx=(z>>>5^y<<2)+(y>>>3^z<<4)^(sum^y)+(k[p&3^e]^z);z=v[n]=v[n]+mx&0xffffffff}return longArrayToString(v,false)}};var keyStr=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\";function encode32(input){input=escape(input);var output=\"\";var chr1,chr2,chr3=\"\";var enc1,enc2,enc3,enc4=\"\";var i=0;do{chr1=input.charCodeAt(i++);chr2=input.charCodeAt(i++);chr3=input.charCodeAt(i++);enc1=chr1>>2;enc2=((chr1&3)<<4)|(chr2>>4);enc3=((chr2&15)<<2)|(chr3>>6);enc4=chr3&63;if(isNaN(chr2)){enc3=enc4=64}else if(isNaN(chr3)){enc4=64}output=output+keyStr.charAt(enc1)+keyStr.charAt(enc2)+keyStr.charAt(enc3)+keyStr.charAt(enc4);chr1=chr2=chr3=\"\";enc1=enc2=enc3=enc4=\"\"}while(i params = new ArrayList();
				params.add(new BasicNameValuePair("randCode",vcode));
				params.add(new BasicNameValuePair("rand", "sjrand"));
				params.add(new BasicNameValuePair("randCode_validate", ""));
				
				get3.setEntity(new UrlEncodedFormEntity(params));
				
				HttpResponse r3 = client.execute(get3);
				
				String r3text = EntityUtils.toString(r3.getEntity(), "GBK");
				
				if(r3text.indexOf("randCodeRight")!=-1){
					
					client.execute(get3);	//这里校验2次
					
					System.out.println("验证码校验成功,正在登录...");
					get3.abort();
					
					/* ************* 登录 开始 ***************** */
					String loginurl = "https://kyfw.12306.cn/otn/login/loginAysnSuggest";
					
					HttpPost post1 = new HttpPost(loginurl);
					
					post1.addHeader("Accept","*/*");
					post1.addHeader("Accept-Language","zh-CN,zh;q=0.8");
					post1.addHeader("Connection","keep-alive");
					post1.addHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8");
					post1.addHeader("Cookie",cookie);
					post1.addHeader("Host","kyfw.12306.cn");
					post1.addHeader("Origin","https://kyfw.12306.cn");
					post1.addHeader("Referer","https://kyfw.12306.cn/otn/login/init");
					post1.addHeader("User-Agent","Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36");
					post1.addHeader("X-Requested-With","XMLHttpRequest");
					
					List post1params = new ArrayList();
					post1params.add(new BasicNameValuePair("loginUserDTO.user_name",username));
					post1params.add(new BasicNameValuePair("userDTO.password", password));
					post1params.add(new BasicNameValuePair("randCode",vcode));
					post1params.add(new BasicNameValuePair("rand", "sjrand"));
					System.out.println(kkky+" - "+vvvy);
					
					/* 这个东西很重要,这个东西就是 动态的一个表单 */
					post1params.add(new BasicNameValuePair(kkky, vvvy));
					post1params.add(new BasicNameValuePair("myversion", "undefined"));
					
					
					post1.setEntity(new UrlEncodedFormEntity(post1params));
					
					HttpResponse r4 = client.execute(post1);
					
					
					String ff = EntityUtils.toString(r4.getEntity(), "UTF-8");
					post1.abort();
					String successResult = "{\"validateMessagesShowId\":\"_validatorMessage\",\"status\":true,\"httpstatus\":200,\"data\":{\"loginCheck\":\"Y\"},\"messages\":[],\"validateMessages\":{}}";
					if(ff.equals(successResult)){
						System.out.println("登录成功   "+successResult);
						
						cookieString = cookie;
						login_key = kkky;
						login_value = vvvy;
						
						return client;
					}else{
						System.out.println("登录失败   "+ff);
					}
					
				}else{
					System.out.println("验证码校验失败");
				}
			}catch(Exception ex){
				ex.printStackTrace();
				System.out.println("下载图片失败");
			}
		}
		return null;
	}

}


好了,大功告成 可以登录了。 

目前登录偶尔会出现 截图所示:第三方购票...


本人原创,转载请注明出处  http://blog.csdn.net/wang_situ/article/details/42262857

功能逐渐完善中,后续会将代码放到github上




你可能感兴趣的:(httpclient)