微信網頁版登陸原理暨查看將本身刪除掉的人項目


項目做用

訪問項目的網頁,掃一掃網頁上的二維碼,就會顯示你的微信好友中將你刪除的人的列表。java


在線網址:

訪問115.29.55.54:8080/WXApi就可使用該項目所說的網頁node


項目原理

在微信中,將你刪掉的好友是沒法加入你建立的羣聊的,而微信網頁版也能夠建立羣聊,因此使用微信網頁版的接口能夠實現分辨一個好友是否是將你刪除了。python


流程和Java實現

1. 獲取UUID

微信在生成二維碼以前,會先生成一個UUID,做爲一個識別的標記,攜帶這個UUID訪問微信的接口就能夠獲取到二維碼。同時也是查看二維碼是否被掃描的一個重要參數。
參數列表以下:git

  • appid (可寫死,wx782c26e4c19acffb)github

  • fun : newweb

  • lang : zh-CN (中國地區)json

  • _ : 時間戳微信

// 參考代碼:
// Java版本
public String getUUID(){
        String url = "https://login.weixin.qq.com/jslogin?appid=%s&fun=new&lang=zh-CN&_=%s";
        url = String.format(url, appID,System.currentTimeMillis());
        httpGet = new HttpGet(url);
        try {
            response = httpClient.execute(httpGet);
            entity = response.getEntity();
            String result = EntityUtils.toString(entity);
            logger.debug(result);
            String[] res = result.split(";");
            if (res[0].replace("window.QRLogin.code = ", "").equals("200")) {
                uuid = res[1].replace(" window.QRLogin.uuid = ", "").replace("\"", "");
                return uuid;
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
# python版本
def getuuid():
    global uuid
    url = 'https://login.weixin.qq.com/jslogin'
    params = {
        'appid': 'wx782c26e4c19acffb',
        'fun': 'new',
        'lang': 'zh_CN',
        '_': int(time.time()),
    }
    request = urllib2.Request(url=url, data=urllib.urlencode(params))
    response = urllib2.urlopen(request)
    data = response.read()

    regx = r'window.QRLogin.code = (\d+); window.QRLogin.uuid = "(\S+?)"'
    pm = re.search(regx, data)

    code = pm.group(1)
    uuid = pm.group(2)

    if code == '200':
        return True

    return False

2. 獲取二維碼

將uuid放在url中而後使用get請求,會收到一個一張二維碼的圖片.固然,若是在網頁中使用<img>的標籤能夠直接將這個URL放進去,就能夠直接顯示一張二維碼。
參數列表以下:app

  • uuid:也就是上面所獲取的UUIDdom

//java版本
// 若是忽略註釋直接返回獲取圖片的url放在網頁中的<img>的標籤下能夠直接顯示,若是使用註釋中的內容會將其下載爲本地圖片
public String getQR(String uuid) {
        if (uuid == null || "".equals(uuid)) {
            return null;
        }
        String QRurl = "http://login.weixin.qq.com/qrcode/" + uuid;
        logger.debug(QRurl);
        return QRurl;
        // 同時提供使其變爲本地圖片的方法
        // httpGet = new HttpGet(QRurl);
        // response = httpClient.execute(httpGet);
        // entity = response.getEntity();
        // InputStream in = entity.getContent();
        // //注意這裏要對filepath賦值
        // OutputStream out = new FileOutputStream(new File("FilePath"+".png"));
        // byte[] b = new byte[1024];
        // int t;
        // while((t=in.read())!=-1){
        // out.write(b, 0, t);
        // }
        // out.flush();
        // in.close();
        // out.close();
    }
# Python版本
def showQRImage():
    global tip
    url = 'https://login.weixin.qq.com/qrcode/' + uuid
    request = urllib2.Request(url=url)
    response = urllib2.urlopen(request)
    f = open(QRImagePath, 'wb')
    f.write(response.read())
    f.close()  # 保存到本地

3. 獲取用戶登陸狀態

登錄狀態主要是兩種,一種是用戶已經掃描,一種是用戶掃描後在手機端已經點擊確認了。這兩種狀態的獲取訪問的url是同樣的,區別是一個叫作tip的參數,當tip=1的時候,若是沒有掃描,服務端會一直等待,若是已經掃描,服務端會返回代買201.當tip=0的時候,若是用戶沒有點擊肯定,那麼就會一直等待,直到用戶點擊肯定後返回200.因此問題來了,若是不改變tip讓他一直爲1也是能夠的,可是就須要不斷的輪詢,而若是改變tip的話,就能夠while的循環。
參數以下:

  • uuid : 就是以前得到的uuid

  • _ : 時間戳

  • tip : 判斷是要得到點擊狀態仍是掃描狀態

  • 狀態=200時,返回值是redirect_url:該返回值是一個url,訪問該url就算是正式的登錄。

//java版本
public int waitForLogin(String uuid, int tip) {
        String urlString = "http://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s";
        urlString = String.format(urlString, tip, uuid, System.currentTimeMillis());
        httpGet = new HttpGet(urlString);
        try {
            response = httpClient.execute(httpGet);
            String re = EntityUtils.toString(response.getEntity());
            String[] result = re.split(";");
            logger.debug(re);
            if (result[0].replace("window.code=", "").equals("201")) {
                tip = 0;
                return 201;
            } else if (result[0].replace("window.code=", "").equals("200")) {
                redirectUri = (result[1].replace("window.redirect_uri=", "").replace("\"", "") + "&fun=new").trim();
                return 200;
            } else {
                return 400;
            }
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return -1;
    }
# python版本
def waitForLogin():
    global tip, base_uri, redirect_uri
    url = 'https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?tip=%s&uuid=%s&_=%s' % (tip, uuid, int(time.time()))
    request = urllib2.Request(url = url)
    response = urllib2.urlopen(request)
    data = response.read()
    regx = r'window.code=(\d+);'
    pm = re.search(regx, data)
    code = pm.group(1)
    if code == '201': #已掃描
        print '成功掃描,請在手機上點擊確認以登陸'
        tip = 0
    elif code == '200': #已登陸
        regx = r'window.redirect_uri="(\S+?)";'
        pm = re.search(regx, data)
        redirect_uri = pm.group(1) + '&fun=new'
        base_uri = redirect_uri[:redirect_uri.rfind('/')]
    elif code == '408': #超時
        pass
    return code

4. 正式登錄

手機端已經受權經過,上一步會返回一個Redirect_Url,這是一個真正的登錄url,使用get方法訪問該url會返回一個xml格式的字符串,其中的屬性將是接下來動做的重要參數。解析該字符串有以下的屬性:

  • int ret;//返回值爲0時表示本次請求成功

  • String message;//一些信息(好比失敗緣由等)

  • String skey;//後面請求會用到的參數

  • String wxsid;//同上

  • long wxuin;// 本人編碼

  • String pass_ticket;//重要!!後面不少請求都會用到這張通行證

  • int isgrayscale;//不明

代碼以下:

//java
private boolean login() {
        String url = redirectUri;
        httpGet = new HttpGet(url);
        try {
            response = httpClient.execute(httpGet);
            entity = response.getEntity();
            String data = EntityUtils.toString(entity);
            logger.debug(data);
            loginResponse = CommonUtil.parseLoginResult(data);
            baseRequest = new BaseRequest(loginResponse.getWxuin(), loginResponse.getWxsid(), loginResponse.getSkey(),
                    loginResponse.getDeviceID());
            return true;
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }
#python版本
def login():
    global skey, wxsid, wxuin, pass_ticket, BaseRequest
    request = urllib2.Request(url = redirect_uri)
    response = urllib2.urlopen(request)
    data = response.read()
    doc = xml.dom.minidom.parseString(data)
    root = doc.documentElement
    for node in root.childNodes:
        if node.nodeName == 'skey':
            skey = node.childNodes[0].data
        elif node.nodeName == 'wxsid':
            wxsid = node.childNodes[0].data
        elif node.nodeName == 'wxuin':
            wxuin = node.childNodes[0].data
        elif node.nodeName == 'pass_ticket':
            pass_ticket = node.childNodes[0].data
    if skey == '' or wxsid == '' or wxuin == '' or pass_ticket == '':
        return False
    BaseRequest = {
        'Uin': int(wxuin),
        'Sid': wxsid,
        'Skey': skey,
        'DeviceID': deviceId,
    }
    return True

5. init初始化

該方法無關緊要,做用主要是初始化幾個聯繫人,多是最近聯繫人仍是怎樣,而且能得到的是登錄人的信息。若是不須要獲取這些東西就能夠跳過這一步。該方法是post方法,但在url中也能夠放幾個值
主要參數:
url中:

  • pass_ticket

  • skey 這兩個參數都是login時的返回值之一

  • r 時間戳

post 文中攜帶:BaseRequst=Json格式的BaseRequest,BaseRequest類中有以下參數:、

  • long Uin;

  • String Sid;

  • String Skey;

  • String DeviceID; DeviceID是一串e開頭的隨機數,隨便填就能夠。

//java
private void initWX() {

        String url = String.format("http://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?pass_ticket=%s&skey=%s&r=%s",
                loginResponse.getPass_ticket(), loginResponse.getSkey(), System.currentTimeMillis());
        InitRequestJson initRequestJson = new InitRequestJson(baseRequest);//Java中包含了BaseRequest的包裝類
        String re = getResponse(url, gson.toJson(initRequestJson));//這是本身寫的一個公有方法,能夠直接看源碼
        InitResponseJson initResponseJson = gson.fromJson(re, InitResponseJson.class);
        mine = initResponseJson.getUser();// 獲取當前用戶信息
    }
def webwxinit():
    url = base_uri + '/webwxinit?pass_ticket=%s&skey=%s&r=%s' % (pass_ticket, skey, int(time.time()))
    params = {
        'BaseRequest': json.dumps(BaseRequest)
    }
    request = urllib2.Request(url=url, data=json.dumps(params))
    request.add_header('ContentType', 'application/json; charset=UTF-8')
    response = urllib2.urlopen(request)
    data = response.read()
    global ContactList, My
    dic = json.loads(data)
    ContactList = dic['ContactList']
    My = dic['User']

    ErrMsg = dic['BaseResponse']['ErrMsg']
    if len(ErrMsg) > 0:
        print ErrMsg

    Ret = dic['BaseResponse']['Ret']
    if Ret != 0:
        return False
    return True

6. 後面部分

因爲登錄成功後後面部分基本就是調用接口了,難點基本沒有,能夠直接看源碼,我在這裏貼上操做步驟

  • 獲取全部的用戶
    經過post方法訪問一個url(源碼中能夠看),就能夠獲取全部的用戶列表。

  • 建立聊天室
    注意一次最多40人不然會出現問題

  • 刪除聊天室的成員

  • 爲聊天室添加成員
    微信會返回該成員的一個狀態,若是狀態等於4,那麼添加失敗,就能夠判斷該用戶已經刪除了登錄用戶。


封裝爲網頁

  1. 獲得uuid,並將其包裝直接插入<img>標籤中就能夠在網頁中顯示該二維碼

  2. 使用AJAX請求,請求waitforlogging()方法,當返回值爲200時成功,此時遍歷該用戶每個好友,判斷其是否刪除了該用戶。

  3. 顯示


參考文檔

  1. 該功能的python實現

  2. 網頁微信登陸原理


項目源碼

項目源碼

相關文章
相關標籤/搜索