java實現QQ空間模擬登陸

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.Map;
import java.util.Scanner;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;

public class QQLogin {

    /*
      http://ptlogin2.qq.com/login?....
         模擬登陸流程分析以及login接口重要參數分析:
          【接口(重要參數) -> 返回的重要信息(說明)】
      一、網上資料都說login_sig參數(來自xlogin接口)挺重要的,也許是tx改版了,我的親測login_sig爲空也行,連各個接口請求頭的cookie都不須要設置。
      二、須要注入的參數有:u,verifycode,pt_vcode_v1,pt_verifysession_v1,p。其餘參數抓包時照搬就好。
      三、u,p: Q號和加密過的密碼。
      四、pt_vcode_v1:這次登陸是否須要驗證碼,不須要則爲0,須要則爲1,與check接口返回樣例的第一個參數一致
      五、verifycode:我的取它名爲真實驗證碼,由於它不是手動輸入的那個驗證碼。
      六、pt_verifysession_v1:真實驗證碼對應的一個session值。
      七、無需輸入驗證碼時比較簡單,verifycode,pt_verifysession_v1這兩個參數直接能從check接口的responseBody中獲取。
         check(u) -> pt_vcode_v1(返回字符串中的第一個參數),verifycode(返回字符串中的第二個參數),pt_verifysession_v1(返回字符串中的第四個參數)
         login(u,verifycode,pt_vcode_v1,pt_verifysession_v1,p) -> 登陸成功cookie
      八、須要輸入驗證碼時複雜不少,由於上面那個兩個參數並無在check接口的返回值中給出,其中有用的是返回值中的第二個參數名爲cap_cd。
         check(u) -> cap_cd(返回字符串中的第二個參數)
         cap_union_getsig_new(cap_cd) -> sig(響應體{"vsig":"SIG","ques":""})
         getimgbysig(sig) -> ans(手動輸入的驗證碼)
         cap_union_verify(ans, sig) -> randstr(即login接口所需的verifycode參數), sig(即login接口所需的pt_verifysession_v1參數)
         login(u,verifycode,pt_vcode_v1,pt_verifysession_v1,p) -> 登陸成功cookie
      九、若上述各個接口的參數沒對上,常見的錯誤提示是驗證碼錯誤!
     */
    
    /**
     * qq空間模擬登陸 main
     * @param args
     * @throws IOException 
     */
    public static void main(String[] args) throws IOException {
        String uin = "2099221914";
        String password = "zzjian";
        String checkStatus = ""; //login接口參數pt_vcode_v1,對應check接口的0,1狀態
        String verifycode = ""; //login接口參數
        String verifysession = ""; //login接口參數
        String p = ""; //login接口參數
        String checkResult = check(uin);
        System.out.println(checkResult);
        if("0".equals(checkResult.charAt(14)+"")) {
            System.out.println("無需驗證碼登陸!");
            checkStatus = "0";
            verifycode = checkResult.split(",")[1].replaceAll("\'", "");
            verifysession = checkResult.split(",")[3].replaceAll("\'", "");
        }
        else {
            System.out.println("須要輸入驗證碼登陸!。");
            checkStatus = "1";
            String cap_cd = checkResult.split(",")[1].replaceAll("'", "");
            String sig = getSig(uin, cap_cd);
            //獲取並輸入驗證碼
            getVerifyCode(uin, sig);
            System.out.println("請輸入驗證碼:");
            Scanner scanf = new Scanner(System.in);
            String vcode = scanf.next(); //輸入驗證碼
            String body = getVerifysession(uin, vcode, sig);
            verifysession = body.split(",")[2].replaceAll("sig:\"", "").replaceAll("\"", "");
            verifycode = body.split(",")[1].replaceAll("randstr:\"", "").replaceAll("\"", "");
            
            while(!body.contains("rcode:0")) {
                sig = refreshSig(uin, sig);
                getVerifyCode(uin, sig);
                System.out.println("error,請從新輸入驗證碼:");
                vcode = scanf.next();
                body = getVerifysession(uin, vcode, sig);
                verifysession = body.split(",")[2].replaceAll("sig:\"", "").replaceAll("\"", "");
                verifycode = body.split(",")[1].replaceAll("randstr:\"", "").replaceAll("\"", "");
            }
        }
        p = encryptPassword(uin, password, verifycode);
        String login_result = login1(uin, p, checkStatus, verifycode, verifysession);
        System.out.println(login_result.split(",")[4]+","+login_result.split(",")[5]);
    }

    public static Map<String, String> cookies;
    
    ////////////////////////////////////////////////////////////////////////////////////////////
    /* 不需驗證碼 begin */
    
    /**
     * 檢查賬號狀態(登陸時是否須要驗證碼)。
     * 若不須要,返回樣例  ptui_checkVC('0','!GWD', '\x00\x....\x29','96b...5wf','0'),
     * 樣例說明:!GWD(真實驗證碼)爲login接口的verifycode參數,'96b...5wf'爲login接口的pt_verifysession_v1參數;
     * 若須要,返回樣例   ptui_checkVC('1','576429...df98', '\x00\x00...f1\x29','','0');
     * 樣例說明:'576429...df98'爲cap_union_show接口的cap_cd參數
     * @return 
     * @throws IOException 
     */
    public static String check(String uin) throws IOException {
        Response response = Jsoup.connect("http://check.ptlogin2.qq.com/check?" +
                                "regmaster=" +
                                "&pt_tea=1" +
                                "&pt_vcode=1" +
                                "&uin=" + uin +
                                "&appid=549000912" +
                                "&js_ver=10140" +
                                "&js_type=1" +
                                "&login_sig=" +
                                "&u1=http%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone" +
                                "&r=0.6051186741306294")
                                  .ignoreContentType(true)
                                  .execute();
        cookies = response.cookies();
        return response.body();
    }
    
    /**
     * 登陸(第一次?)
     * @return 登陸成功時返回字符串:ptuiCB('0','0','http://web.qq.com/loginproxy.html?login2qq=1&webqq_type=10','0','登陸成功','你的名字');
     * @throws IOException 
     */
    public static String login1(String uin, String p, String checkStatus, String verifycode, String verifysession) throws IOException {
        Response response = Jsoup.connect("http://ptlogin2.qq.com/login?" +
                                "u=" + uin +
                                "&verifycode=" + verifycode +
                                "&pt_vcode_v1=" + checkStatus +
                                "&pt_verifysession_v1=" + verifysession +
                                "&p="+p+
                                "&pt_randsalt=0" +
                                "&u1=http%3A%2F%2Fqzs.qq.com%2Fqzone%2Fv5%2Floginsucc.html%3Fpara%3Dizone" +
                                "&ptredirect=0" +
                                "&h=1" +
                                "&t=1" +
                                "&g=1" +
                                "&from_ui=1" +
                                "&ptlang=2052" +
                                "&action=2-1-1447938345482" +
                                "&js_ver=10140" +
                                "&js_type=1" +
                                "&login_sig=" +
                                "&pt_uistyle=32" +
                                "&aid=549000912" +
                                "&daid=5&")
                                .ignoreContentType(true)
                                  .execute();
        cookies.putAll(response.cookies());
        return response.body();
    }
    /* 不需驗證碼 end */
    /////////////////////////////////////////////////////////////////////////////////////////////
    
    /**
     * 返回值sig爲getimgbysig接口(獲取驗證碼接口)所需參數,
     * 同時也是cap_union_verify接口(校驗驗證碼正確性接口)所需參數。
     * @param uin
     * @param cap_cd
     * @return
     * @throws IOException
     */
    public static String getSig(String uin, String cap_cd) throws IOException {
        Response sigResponse = Jsoup.connect("http://captcha.qq.com/cap_union_getsig_new?" +
                                    "clientype=2" +
                                    "&captype=" +
                                    "&protocol=http" +
                                    "&disturblevel=" +
                                    "&apptype=2" +
                                    "&noBorder=noborder" +
                                    "&showtype=embed" +
                                    "&rnd=181847" +
                                    "&aid=549000912" +
                                    "&uin=" + uin +
                                    "&cap_cd=" + cap_cd +//由check接口響應得到
                                     "&rand=0.5029603082194563")
                                     .execute();
        String body = sigResponse.body();
        String temp = body;
        String beginString = "{\"vsig\":\"";
        String sig = temp.substring(temp.indexOf(beginString)+beginString.length(), temp.indexOf("\",\""));
        return sig;
    }
    
    /**
     * 刷新sig,用來獲取新的驗證碼圖片
     * @param oldSig
     * @return 返回樣例:cap_setQue("",0);cap_showOption(""); cap_getCapBySig("gOCP..m4-8CswYA**");
     * @throws IOException
     */
    public static String refreshSig(String uin, String oldSig) throws IOException {
        Response response = Jsoup.connect("http://captcha.qq.com/getQueSig?" +
                                    "aid=549000912" +
                                    "&uin=" + uin +
                                    "&captype=2" +
                                    "&sig=" + oldSig +
                                    "&0.6583711083512753")
                                    .execute();
        //截取結果中cap_getCapBySig("gOCP..m4-8CswYA**")引號部分
        String newSig = response.body().split(";")[2].split("\"")[1];
        return newSig;
    }
    
    /**
     * 保存驗證碼圖片
     * @param
     * @throws IOException 
     */
    public static void getVerifyCode(String uin, String sig) throws IOException {
        Response imgResponse = Jsoup.connect("http://captcha.qq.com/getimgbysig?" +
                                "uin="+uin+
                                "&aid=549000912" +
                                "&sig="+sig)
                                .ignoreContentType(true)
                                .execute();
        File imge = new File("resources/VerifyCode.jpg");
        FileOutputStream out = new FileOutputStream(imge);
        out.write(imgResponse.bodyAsBytes());
        out.close();
    }
    
    /**
     * 校驗驗證碼正確性,返回結果包含rcode:0,則驗證碼輸入正確
     * @param uin
     * @param verifycode 填寫的驗證碼
     * @param sig
     * @return 正確時返回樣例 : cap_InnerCBVerify({rcode:0,randstr:"@fmP",sig:"t02...aP12",errmsg:"..........................."})
     *             從中獲取 randstr:"@AIU"(真實驗證碼),以及sig:"..."(login接口所需的pt_verifysession_v1參數)。
     *          驗證碼錯誤返回樣例: cap_InnerCBVerify({rcode:5,randstr:"",sig:"",errmsg:"驗證失敗,請重試。"});
     * @throws IOException
     */
    public static String getVerifysession(String uin, String verifycode, String sig) throws IOException {
        Response response = Jsoup.connect("http://captcha.qq.com/cap_union_verify?" +
                                 "aid=549000912" +
                                 "&uin=" + uin +
                                 "&captype=50" +
                                 "&ans=" + verifycode +
                                 "&sig=" + sig +
                                 "&0.49537746398709714")
                                 .execute();
        return response.body();
    }
    
    /**
     * 密碼加密,經過調用js函數加密
     * @param verifycode 真實驗證碼,非手動輸入的那個驗證碼
     * @return 返回加密後的密碼,login接口所需的p參數
     */
    public static String encryptPassword(String uin, String password, String verifycode) {
        String p_result = "";
        try {
            ScriptEngineManager sem = new ScriptEngineManager();
            ScriptEngine engine = sem.getEngineByName("js");
            FileReader fr = new FileReader("resources/login.js");
            engine.eval(fr);
            Invocable inv = (Invocable) engine;
            p_result = inv.invokeFunction("getEncryption", password,
                    uin, verifycode).toString();
        } catch (ScriptException e1) {
            e1.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e2) {
            e2.printStackTrace();
        }
        return p_result;
    }

    /**
     * 第二次登陸
     * @return
     * @throws IOException
     */
    public static String login2() throws IOException {
        Response response = Jsoup.connect("")
                                 .ignoreContentType(true)
                                 .execute();
        return response.body();
    }
    
}

 

2016-11-27html

代碼中用到的模擬發請求的類庫:http://files.cnblogs.com/files/zhangzongjian/jsoup.rarjava

代碼中調用的js:http://files.cnblogs.com/files/zhangzongjian/login1.jsweb

這個js是網上找來的,我也忘了是那個網站了。js是密碼加密用的,是別人分析tx的js提取出來的精華,由於用tx的那個js文件用java自帶的解析引擎解析語法報錯。cookie

相關文章
相關標籤/搜索