=====================================
本文參考 《挖掘微信Web版通信的全過程》
代碼參考 Python網頁微信API
=====================================
0.說明
前人已經做了大量工作,作者在此基礎上略加更新修改,適用於微信6.5.4。
1.微信服務器返回一個會話ID(uuid)
微信Web版本不使用用戶名和密碼登錄,而是采用掃描二維碼登錄,所以服務器需要首先分配一個唯一的會話ID,用來標識當前的一次登錄,通過請求地址:
https://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&fun=new&lang=zh_CN&_=1377482012272(其中1377482012272這個值是當前距離林威治標准時間的毫秒);
請求成功服務器會返回如下的字符串:
window.QRLogin.code = 200; window.QRLogin.uuid = "IZrc7nK6TQ=="
200表示正確返回,而這個IZrc7nK6TQ==字符串就是微信服務器返回給我們的ID(uuid)。
2.通過會話ID獲得二維碼
既然微信Web版本是通過二維碼進行登錄,如何獲得這個隨機的二維碼呢?答案就是通過上面的ID我們組合得到以下的URL地址:
https://login.weixin.qq.com/l/IZrc7nK6TQ==
不需做任何HTTP請求,直接將這個URL地址作為文本信息生成二維碼,此時需要用戶在微信的手機版本中掃描這個二維碼。
3.輪詢手機端是否已經掃描二維碼並確認在Web端登錄
當獲得二維碼之后,就需要用戶去手機端去掃描二維碼,並獲得用戶的授權,此時我們並不知道用戶何時完成這個操作,所以我們只有輪詢,而輪詢的地址就是:
https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid=IZrc7nK6TQ==&tip=1&_=1377482045264
注意tip的值是變化的。首次設置tip=1。
如果服務器返回:window.code=201
則說明此時用戶在手機端已經完成掃描,但還沒有點擊確認,再次輪詢時設置tip=0;
如果服務器返回:window.code=200
window.redirect_uri="https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxnewloginpage?ticket=A5ncNUM2NJBYNOpJ49Jd38m2@qrticket_0&uuid=Ia7HTPkEdQ==&lang=zh_CN&scan=1485320697"
則說明此時用戶在手機端確認登錄,保存下window.redirect_uri=這個URL地址,供下一步驟中使用。
如果服務器返回:window.code=408
等待超時,再次輪詢時設置tip=1;
4.訪問登錄地址,獲得uin、sid、pass_ticket、skey
通過訪問上一步驟中獲得的URL地址,可以獲取uin、sid、pass_ticket、skey,在后續的通信過程中都要使用到這幾個值。
5.初使化微信信息
前面的步驟算是完成了這個復雜的登錄過程,如果我們需要使用微信就需要獲得當前用戶的信息、好友列表等,還有一個關鍵的就是同步信息(后續與服務器輪詢中需要使用同步信息),通過URL進行HTTP POST:
https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=1486118990309&lang=en_US&pass_ticket=gzP3GEH0awvApwIttyTwxzAvA27%2BwVAp9tQ1osM%2FLL90XWWU5JeIdNLLVjN%2BJ9bq
POST的內容為以下的JSON信息:
{"BaseRequest":{"Uin":"2897899826","Sid":"5Lo1OrZQix+sduZT","Skey":"@crypt_dcaca546_c69b06b40828731a0acb3235758c0ea6","DeviceID":"e9803127325048260"}}
DeviceID是一個本地生成的隨機字符串(e+十六位的一串數字)。
服務器會返回一個很長的JSON串,這其中包括:用戶信息以及同步鍵值,SyncKey是用戶與服務器同步的鍵值,User就是當前登錄用戶自己的信息。
6.獲得所有的好友列表
在上一步驟中已經獲得了部分好友和公眾帳號,如果需要獲得完整的好友信息,就需要訪問以下的鏈接:
https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?pass_ticket=gzP3GEH0awvApwIttyTwxzAvA27%2BwVAp9tQ1osM%2FLL90XWWU5JeIdNLLVjN%2BJ9bq&skey=@crypt_dcaca546_c69b06b40828731a0acb3235758c0ea6&r=1486119544662
保持之前訪問的Cookies不被修改,在返回的JSON串中,MemberList中就包含了所有的好友信息。
7.保持與服務器的信息同步
與服務器保持同步需要在客戶端做輪詢,該輪詢的URL如下:
https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck?sid=5Lo1OrZQix+sduZT&uin=2897899826&synckey=1_652651920%7C2_652651939%7C3_652651904%7C1000_0&r=1486119893602&skey=@crypt_dcaca546_c69b06b40828731a0acb3235758c0ea6&deviceid=e9803127325048260&_=1486119893618
其中的參數r和_都是time,sid,uin,skey,deviceid與上面步驟的值相對應,此處的synkey是第五步獲得的同步鍵值,但需要按一定的規則組合成以下的字符串:
1_652653204|2_652653674|3_652653544|1000_0
就是將鍵和值用_隔開,不同的鍵值對用|隔開,但記得|需要URL編碼成%7C,通過訪問上面的地址,會返回如下的字符串:
window.synccheck={retcode:”0”,selector:”2”}
如果retcode中的值不為0,則說明與服務器的通信有問題了。selector中的值表示客戶端需要作出的處理,當為2的時候表示有消息來了,就需要去訪問另一個接口獲得新的消息。
8.獲得別人發來的消息
當一個步驟中知道有新消息時,就需要去獲取消息內容,通過訪問以下的鏈接:
https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=5Lo1OrZQix+sduZT&lang=zh_CN&skey=@crypt_dcaca546_c69b06b40828731a0acb3235758c0ea6&pass_ticket=gzP3GEH0awvApwIttyTwxzAvA27%2BwVAp9tQ1osM%2FLL90XWWU5JeIdNLLVjN%2BJ9bq
上面鏈接中的參數sid對應上面步驟中的值,r為時間,訪問鏈接需要使用POST方式,Body中包括JSON串,該JSON串格式如下:
{"BaseRequest" : {"DeviceID":"e9803127325048260","Sid":"5Lo1OrZQix+sduZT", "Skey":"@crypt_dcaca546_c69b06b40828731a0acb3235758c0ea6", "Uin":"2897899826"},"SyncKey" : {"Count":4,"List":[{"Key":1,"Val":652653204},{"Key":2,"Val":652653674},{"Key":3,"Val":652653544},{"Key":1000,"Val":0}]},"rr" :1486120093943}
請求成功之后服務器會返回一個JSON串,其中AddMsgList中是一個數組,包含了所有新消息。
9.向用戶發送消息
用戶主動發送消息,通過以下的URL地址:
https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket=gzP3GEH0awvApwIttyTwxzAvA27%2BwVAp9tQ1osM%2FLL90XWWU5JeIdNLLVjN%2BJ9bq
訪問該URL采用POST方式,在Body中的JSON串形如以下的格式:
{"Msg":{"Type":1,"Content":"hello world","FromUserName":"@fddaafca276a134rsdf234367fdfsdf6748a0843e6099de91","ToUserName":"@fdda235sfsabd67fas79f55c8a0843eas91","LocalID":"14851203867025441","ClientMsgId":"14851203867025441"},"BaseRequest":{"Uin":"2897899826","Sid":"5Lo1OrZQix+sduZT","Skey":"@crypt_dcaca546_c69b06b40828731a0acb3235758c0ea6","DeviceID":"e9803127325048260"}}
其中BaseRequest都是授權相關的值,與上面的步驟中的值對應,Msg是對消息的描述,包括了發送人與接收人,消息內容,消息的類型,ClientMsgId和LocalID由本地生成。rr可用當前的時間。
在返回JSON結果中BaseResponse描述了發送情況,Ret為0表示發送成功。