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接口重要参数分析:
          【接口(重要参数) -> 返回的重要信息(说明)】
      1、网上资料都说login_sig参数(来自xlogin接口)挺重要的,也许是tx改版了,个人亲测login_sig为空也行,连各个接口请求头的cookie都不需要设置。
      2、需要注入的参数有:u,verifycode,pt_vcode_v1,pt_verifysession_v1,p。其他参数抓包时照搬就好。
      3、u,p: Q号和加密过的密码。
      4、pt_vcode_v1:此次登录是否需要验证码,不需要则为0,需要则为1,与check接口返回样例的第一个参数一致
      5、verifycode:个人取它名为真实验证码,因为它不是手动输入的那个验证码。
      6、pt_verifysession_v1:真实验证码对应的一个session值。
      7、无需输入验证码时比较简单,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
      8、需要输入验证码时复杂很多,因为上面那个两个参数并没有在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
      9、若上述各个接口的参数没对上,常见的错误提示是验证码错误!
     */

    /**
     * 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-27

代码中用到的模拟发请求的类库:http://files.cnblogs.com/files/zhangzongjian/jsoup.rar

代码中调用的js:http://files.cnblogs.com/files/zhangzongjian/login1.js

这个js是网上找来的,我也忘了是那个网站了。js是密码加密用的,是别人分析tx的js提取出来的精华,因为用tx的那个js文件用java自带的解析引擎解析语法报错。

java实现QQ空间模拟登录的更多相关文章

  1. Java版 QQ空间自动登录无需拷贝cookie一天抓取30WQQ说说数据&流程分析

    QQ空间说说抓取难度比较大,花了一个星期才研究清楚! 代码请移步到GitHub GitHub地址:https://github.com/20100507/Qzone [没有加入多线程,希望你可以参与进 ...

  2. 1、IOS开发--iPad之仿制QQ空间(登录界面搭建+登录逻辑实现)

    开始搭建登录界面 登录界面效果图: 相关的图片资源下载百度云备份链接: http://pan.baidu.com/s/1o71cvMU 密码: 2h7e 步骤开始: 设置辅助窗口的位置在下方 快捷键o ...

  3. Java豆瓣电影爬虫——模拟登录的前世今生与验证码的爱恨情仇

    前言 并不是所有的网站都能够敞开心扉让你看个透彻,它们总要给你出些难题让你觉得有些东西是来之不易的,往往,这也更加激发你的激情和斗志! 从<为了媳妇的一张号,我与百度医生杠上了>里就有网友 ...

  4. JS/java实现QQ空间自动点赞

    使用方法: 1:进入QQ空间 2:复制下面代码 3:按F12或右键审查元素 进入控制台 也就是console 4:粘贴  回车键  喝口水 5:如果嫌慢的话可以 修改这段代码. window.setI ...

  5. Python案例之QQ空间自动登录程序实现

    不多说,直接上干货! 工具选择: 电脑系统:win7,32 位,下面第二部安装SetupTools时注意系统版本要求: Python: 2.7.11,  相信只要是2.7的就可以实现: Seleniu ...

  6. selenium模拟登录豆瓣和qq空间

    selenium模拟登录豆瓣和qq空间今天又重新学习了下selenium,模拟登录豆瓣,发现设置等待时间真的是很重要的一步,不然一直报错:selenium.common.exceptions.NoSu ...

  7. 参数化登录QQ空间实例

    通过参数化的方式,登录QQ空间 实例源码: # coding:utf-8 from selenium import webdriver import unittest import time clas ...

  8. QQ空间说说爬虫

    QQ空间说说爬虫 闲来无事,写了一个QQ空间的爬虫,主要是爬取以前的说说,然后生成词云. 这次采用的主要模块是selenium,这是一个模拟浏览器的模块,一开始我不想用这个模块写的,但是后面分析的时候 ...

  9. QQ空间动态内容,好友信息,点赞爬虫脚本

    一.安装基础的软件包: 1.准备好火狐浏览器,并下载geckodriver,将geckodriver加入到环境变量:下载geckodriver的地址:https://pan.baidu.com/s/1 ...

随机推荐

  1. 使用ZooKeeper实现配置同步(转)

    前言 应用项目中都会有一些配置信息,这些配置信息数据量少,一般会保存到内存.文件或者数据库,有时候需要动态更新.当需要在多个应用服务器中修改这些配置文件时,需要做到快速.简单.不停止应用服务器的方式修 ...

  2. POJ 1084

    WA了好久,第一次用重覆盖的模型做题.感觉这题有个陷阱,那就是当去掉某些边后,若因为这个边去掉而被破环的正方形还存在,那么就会造成覆盖不完全,WA. 所以,在去掉边后,必定有些正方形是不存在的,须重新 ...

  3. 怎样预置Android 手机 APK

    预制APK有下面4种情况: 1, 怎样将带源代码的 APK 预置进系统? 2, 怎样将无源代码的APK预置进系统? 3, 怎样预置APK使得用户能够卸载,恢复出厂设置时不能恢复? 4, 怎样预置APK ...

  4. Android上传图片之调用系统拍照和从相冊选择图片

    Android上传图片之调用系统拍照和从相冊选择图片 本篇文章已授权微信公众号 guolin_blog (郭霖)独家公布 前言: 万丈高楼平底起,万事起于微末.不知不觉距离上篇博文已近四个月,2015 ...

  5. SpringMVC 拦截器不拦截静态资源的三种处理方式方法

    方案一.拦截器中增加针对静态资源不进行过滤(涉及spring-mvc.xml) <mvc:resources location="/" mapping="/**/* ...

  6. Windows 10彻底关闭自动更新

    关键点:把流量计费开启.

  7. android的低内存管理器【转】

    本文转载自:http://blog.csdn.net/haitaoliang/article/details/22092321 版权声明:本文为博主原创文章,未经博主允许不得转载. 安卓应用不用太在意 ...

  8. Android进程回收机制LMK(Low Memory Killer)【转】

    本文转载自:http://www.cnblogs.com/wytiger/p/5744752.html 熟悉Android系统的童鞋都知道,系统出于体验和性能上的考虑,app在退到后台时系统并不会真正 ...

  9. 国内物联网平台初探(七) ——Ablecloud物联网自助开发和大数据云平台

    平台定位 面向IoT硬件厂商,提供设备联网与管理.远程查看控制.定制化云端功能开发.海量硬件数据存储与分析等基础设施,加速硬件实现联网智能化. 架构 服务 云端服务一体化开发引擎 业内独创一体化开发引 ...

  10. 【HDU 1846】 Brave Game

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=1846 [算法] 巴什博弈 若有(m+1)个石子,显然先手不能直接取完,后手必胜 因此,我们可以把石 ...