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