★我要吧★

 找回密码
 注册[Register]
搜索
qq空间相册密码查看为什么登陆后需要激活无法注册?

[技术] 挖掘微信Web网页版通信的全过程

[复制链接]
发表于 2016-2-14 09:06:30 | 显示全部楼层 |阅读模式
昨天是周末,在家闲得无聊,于是去weiphone逛了一圈,偶然发现有人发了一帖叫《微信 for Mac》,这勾起了我的好奇心,国内做Mac开发的人确实很少,对于那些能够独自开发一些Mac第三方工具的开发者我都表示很敬畏,于是点进去看了一个究竟,如果你们好奇也可以点进去看个明白,我最终得出的结论就是:坑爹呢这是!直接用一个WebView去加载了wx.qq.com这个网页也敢自称是微信For Mac?对于这种欺骗用户的行为我十分不屑,同时也让我在思考在微信不提供API的环境下开发一款原生的微信Mac版本是否可行,最有可能的就是去分析微信Web版本的通信过程,然后在程序中模拟这个流程,在我苦苦研究了一个下午之后,终于摸透了这个过程,并用程序实现了大部分功能,下面就详细解说一下微信Web版的流程:% ?8 N- D! U4 h# W8 d- G, T& Y
8 U. j! E/ P9 x1 U' A" A
1.微信服务器返回一个会话ID. y8 S) t2 F; {( p  j
' H# h- j+ _% k8 w. A1 I( K; i
微信Web版本不使用用户名和密码登录,而是采用二维码登录,所以服务器需要首先分配一个唯一的会话ID,用来标识当前的一次登录,通过请求地址:
% ^- h5 S; J8 @/ O/ F* w6 S2 T
7 W- q8 B, e2 b" {, ohttps://login.weixin.qq.com/jslogin?appid=wx782c26e4c19acffb&redirect_uri=https%3A%2F%2Fwx.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=1377482012272(其中1377482012272这个值是当前距离林威治标准时间的毫秒)( T" p3 i1 b9 Q/ ?% X

, u6 R# w9 }9 g, X  q" C6 J服务器会返回如下的字符串:' o- E! D. [$ C8 \; f& ^2 d$ x3 j2 V

7 S" `, I4 M* O- P+ X) rwindow.QRLogin.code = 200; window.QRLogin.uuid = “DeA6idundY9VKn”;# q- D- g7 g! ~
% o" D. N+ \! n1 X! Y
而这个DeA6idundY9VKn字符串就是微信服务器返回给我们的ID。# }+ T  b, t6 X# S( e' g
* ?$ i5 E) L) [2 C( _% }7 Z
2.通过会话ID获得二维码
8 x# \1 q4 f! G, [
/ q# l) K3 }( U( h' T% q既然微信Web版本是通过二维码进行登录,如何获得这个随机的二维码呢?答案就是利用刚才获得的ID去请求服务器生成的二维码,通过上面的ID我们组合得到以下的URL地址:7 v- C. U: D9 ~! O
% d, _- Z% T, ~8 J. ?
https://login.weixin.qq.com/qrcode/DeA6idundY9VKn?t=webwx$ R5 x  }9 V9 i+ x1 ]
! P, P) E( D- z9 M5 a+ j6 l/ L
该请求返回的便是我们需要的二维码,此时需要用户在微信的手机版本中扫描这个二维码(我就搞不明白微信官方是如何想的,登录Web版本竟然还需要手机微信去配合登录,难道没有考虑我被迫选择Web微信就是因为手机不在身边这样的情形么?)# h5 ]0 p% @) p6 a( C) ?0 [6 |
  {. r' l4 [; ?- K# d- R% s2 h6 Q
3.轮询手机端是否已经扫描二维码并确认在Web端登录
4 ^2 S( B. S- r( T& \7 p& ^  t
/ O0 ]0 X; S( E2 R& ?( L当获得二维码之后,就需要用户去手机端去扫描二维码,并获得用户的授权,此时我们并不知道用户何时完成这个操作,所以我们只有轮询,而轮询的地址就是:& D9 q' E6 f% x! i0 P7 ^: ^# J5 S
$ \; B5 l* w. T, Z, g
https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid=DeA6idundY9VKn&tip=1&_=1377482045264(注意UUID和最后时间这两个参数)9 k0 z# W7 D' t( X! c. T% c3 b

; x, C% v) n9 X/ i# f' J如果服务器返回:8 J$ Y' W( t2 ?. b
+ m' n9 A, U- W2 V
window.code=201;
% H) T" t% r+ \) b% R  d/ L. n) \, K( h+ G. e
则说明此时用户在手机端已经完成扫描,但还没有点击确认;
" z  ?1 _! U  O* c& w$ F$ ]; B4 |
# R6 m) D: |9 ^( J2 B如果服务器返回:1 j$ i7 U  |0 \8 V1 `, G
! |8 _6 Y/ _/ h  Z3 x, c8 W& K6 t
window.redirect_uri=一个URL地址
9 A; G5 Z6 {2 F' Q, O9 g9 j# R- d) G. N  J
则说明此时用户已经在手机端完成了授权过程,保存下这个URL地址下一步骤中使用。
/ s8 q* I, q  ?- ?5 g# ?) S: h4 ]1 @" ]: B# i& I
4.访问登录地址,获得uin和sid2 {  S) X& J$ t8 M8 B0 j$ _
" {5 B5 M) K$ O! }
通过访问上一步骤中获得的URL地址,可以在服务器返回的Cookies中获得到wxuin和wxsid这两个值,这两值在后续的通信过程中都要使用到这两个值,并且Cookies中也需要包括这两项。  X, M% f0 s' g0 S+ t$ s2 Z

8 x0 k2 e+ _5 d# a5 v3 [5.初使化微信信息
4 r' l  t1 ?; _4 W* \4 m: j2 B: y1 D2 ?2 @- x3 N$ |
前面的步骤算是完成了这个复杂的登录过程,如果我们需要使用微信就需要获得当前用户的信息、好友列表等,还有一个关键的就是同步信息(后续与服务器轮询中需要使用同步信息),通过访问以下的链接:
# j5 _2 D- M8 K2 d3 [: |/ j
: q1 A7 W# H( o9 e. A% Dhttps://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=1377482058764(r依然是时间)6 z1 u4 [- T2 r9 G- q4 s/ J

* J" @! A: O, h0 u访问该链接需要使用POST,并且在Body中带上以下的JSON信息:
3 _- G8 }# t. {5 c2 D8 a# h# h; _% Z$ k
1; A( [% D4 C  Z% [. i
2
$ q% W' y9 A$ S& h! p{"BaseRequest":2 o) C5 i- C1 k8 k7 r5 m
{"Uin":"2545437902","Sid":"QfLp+Z+FePzvOFoG","Skey":"","DeviceID":"e1615250492"}}2 P* [( G+ K2 q$ F$ O% s; x3 I7 N2 @
这个JSON串中Uin和Sid分别是上面步骤中获得的那两个Cookie值,DeviceID是一个本地生成的随机字符串(分析了官方的总是e+一串数字,所以我们也保持这样的格式)。
8 s: l- I" A9 y$ d8 |1 d( x0 D, r6 x2 g& G( F
服务器就会返回一个很长的JSON串,这其中包括:BaseResponse中的值用来表示请求状态码,ContactList主要用来表示联系人(此列表不全,只包括了类似通讯录助手、文件助手、微信团队和一些公众帐号等,后面会通过另一接口去获得更全面的信息),SyncKey是用户与服务器同步的信息,User就是当前登录用户自己的信息。
- w! B7 q4 k  X0 M8 r3 E+ E# |
6.获得所有的好友列表
8 I/ X% i' c, ?+ p( W0 T
* V4 C" S2 Q1 _+ n7 Y6 R3 N在上一步骤中已经获得了部分好友和公众帐号,如果需要获得完整的好友信息,就需要访问以下的链接:( S8 K9 J. J! L. V5 H0 V
+ i! p2 [6 O6 X0 v- }% m2 u# w9 @4 d
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?r=1377482079876(r依然是时间)3 Y' l* @) O5 O0 d! }# W+ O" a

4 f+ }5 K8 q9 b1 ^* V) a* _+ Y访问该链接同样需要POST方式,但Body为空JSON:{},服务器对身份的判定是通过Cookies,所以需要保持之前访问的Cookies不被修改(在Objective-C中会自动保存相关的Cookies,无需程序特殊处理),在返回的JSON串中,MemberList中就包含了所有的好友信息。# D. n* C( K: F. k+ `  h

; K( i# P% X. O* H! x( z7.保持与服务器的信息同步! i7 Y! M7 T7 m) o) y

, d7 s8 I: {4 G4 r5 y0 g# z8 V与服务器保持同步需要在客户端做轮询,该轮询的URL如下:
( }+ k: G$ b9 [; A& h# N* X; ?
( `1 J+ @$ b% Thttps://webpush.weixin.qq.com/cgi-bin/mmwebwx-bin/synccheck?callback=jQuery18309326978388708085_1377482079946&r=1377482079876&, p& J% Q" ]" X, v% ~
sid=QfLp+Z+FePzvOFoG&uin=2545437902&deviceid=e1615250492&synckey=(见以下说明)&_=13774820798763 |! g4 ~' n* H. H

' B' R1 b4 L* U& \4 ^( r, w6 K: @' Z其中的参数r和_都是time,sid,uin,deviceid与上面步骤的值相对应,此处的synkey是上步步骤获得的同步键值,但需要按一定的规则组合成以下的字符串:
4 `0 |. h' j7 O- m5 ?
% n. S* Q0 y' }1_124125|2_452346345|3_65476547|1000_5643635
! ?2 \) Q6 }' _. J) c
) w5 X' j9 `8 I' W& O6 a就是将键和值用_隔开,不同的键值对用|隔开,但记得|需要URL编码成%7C,通过访问上面的地址,会返回如下的字符串:* Z1 Z& t/ p+ S6 H5 {
6 p; E6 C6 E8 f  B; Y
window.synccheck={retcode:”0”,selector:”0”}1 u% U. y" t7 K' O* j

& ~" V, C# Y8 l& O6 s. \如果retcode中的值不为0,则说明与服务器的通信有问题了,但具体问题我就无法预测了,selector中的值表示客户端需要作出的处理,目前已经知道当为6的时候表示有消息来了,就需要去访问另一个接口获得新的消息。
% o- R, ^5 p' x2 ^, D3 g: M* Q) h1 ?# K' W- n  Z' K7 ?
8.获得别人发来的消息
, }, g0 s3 {5 I3 c9 a% P' x
: V) J& T4 j1 {: W% q, N当一个步骤中知道有新消息时,就需要去获取消息内容,通过访问以下的链接:
$ v3 w, p& @5 ^% X2 M
7 H* k) i! [6 ^3 V3 O6 a- ?https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=QfLp+Z+FePzvOFoG&r=1377482079876
/ U  R! x2 E7 T. L  V
1 Y3 f9 @: ]( E4 |: P上面链接中的参数sid对应上面步骤中的值,r为时间,访问链接需要使用POST方式,Body中包括JSON串,该JSON串格式如下:% t; c5 i5 f  }* g9 c* C

; x& O) `; _3 I, P1
1 ~8 ~+ x: H- @4 _+ e0 R: w2, K+ H% C' Q3 e1 p3 S1 Z
3; K! ?  r, f+ H7 r
{"BaseRequest" : {"Uin":2545437902,"Sid":"QfLp+Z+FePzvOFoG"},) m( T( M3 F  V* O0 a& F; z( w* c
"SyncKey" : {"Count":4,"List":[{"Key":1,"Val":620310295},{"Key":2,"Val":620310303},{"Key":3,"Val":620310285},{"Key":1000,"Val":1377479086}]},
9 X0 u) G" Q8 U' m- c4 N/ O"rr" :1377482079876};
# f8 r5 Z0 [  V9 M: R8 G1 f/ z* Q以下的信息中BaseRequest中包括的Uin与Sid与上面步骤中的值对应,SyncKey也是上面步骤中获得的同步键值对,rr为时间,访问成功之后服务器会返回一个JSON串,其中AddMsgList中是一个数组,包含了所有新消息。( J6 m9 C) f/ |/ J( ^* F9 ]" {

' P- C4 S9 w9 s4 j; t! t9.向用户发送消息# B+ f  X3 Q( {6 W0 _
- G5 G; Q4 A. n' S- b; W
用户主动发送消息,通过以下的URL地址:* i" g: C4 z2 F9 j. j9 j
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?sid=QfLp+Z+FePzvOFoG&r=13774820798768 H2 M- v8 J6 N# g. |
上面的sid和r参数不再解释了,访问该URL采用POST方式,在Body中的JSON串形如以下的格式:
# m0 J4 F( B- ?2 b
: c/ B; J! ]& c, M6 ?+ N15 c  ]1 {( n* m* M! d
20 ^; ]4 B1 V4 \8 }2 X  I# h
37 Q) D3 [: R$ l3 F
48 w8 [9 Y( ^8 {' N& ^2 i
50 `8 j& ]/ F  M1 L. a6 k4 R
65 j) d- B- o2 r) A: k! _. o
7
& J4 ?! n" [3 v" v8+ [, \0 j0 B# X0 r2 u" C9 S
9
% t) T2 E" b/ w% u6 T% h10
) q) ?1 G  t- U- K% Q7 W11
0 ^' u' e% a! i12/ h# @6 S  }& m$ @& Y2 a; W
13
- H5 X  I% Q( x; n$ |3 q7 S14
& U: p* F: ^% h5 M1 ~15
6 e; W3 H9 b9 i7 R$ F8 J4 A16. _5 u+ j5 F: C; S6 Z
17  t- l$ F  L. F( i' ^
{
0 Q8 p5 C5 L0 G- ^& m" D    "BaseRequest":{4 t  F, n5 a* B# j  V/ @' V/ a
        "DeviceID" : "e441551176",
$ |  L" W/ ]9 g4 X        "Sid" : "S8wNi91Zry3024eg",8 Z' C2 C! Z. z
        "Skey" : "F820928BBA5D8ECA23448F076D2E8A915E1349E9FB4F4332",
8 h' M) w$ u) `2 @! ^        "Uin" : "2545437902"$ w4 J) H2 ?( Z5 `. Q
    },
2 _- ?; e% S9 p3 `% n8 G# U1 ?' _1 H    "Msg" : {
- u' n" X7 C: l! ^        "ClientMsgId" : 1377504862158,, b/ p6 L, N" a1 p
        "Content" : "hello",/ Y* C- [; G! |$ I9 z) j6 g- @
        "FromUserName" : "wxid_2rrz8g8ezuox22",
, g; i9 ~. T/ x7 Q4 b7 ?. c        "LocalID" : 1377504862158,' G7 E& B, O" g! b4 j
        "ToUserName" : "wxid_j4nu420ojhsr21",
! R$ h# v3 j2 i" R& y' {- |: l        "Type" : 1+ Q8 @, a9 z4 u5 o/ P
    },
: S( R' E! L! X9 e5 ]/ E: I' [2 E    "rr" = 1377504864463; j  R8 i) [: s8 i( u1 t2 ?
}$ a- ]: v% T4 f/ `% f$ O( D
其中BaseRequest都是授权相关的值,与上面的步骤中的值对应,Msg是对消息的描述,包括了发送人与接收人,消息内容,消息的类型(1为文本),ClientMsgId和LocalID由本地生成。rr可用当前的时间。
/ c( g& e9 _0 j在返回JSON结果中BaseResponse描述了发送情况,Ret为0表示发送成功。
6 M5 y( h: E( c, P5 P3 Z$ j# x: b7 Y: ]0 D2 n
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

QQ|手机版|小黑屋|☆我要吧☆ ( 豫ICP备13016831号-1 )

GMT+8, 2025-12-14 20:10 , Processed in 1.595374 second(s), 18 queries .

Powered by abc369 X3.4

© 2001-2023 abc369.

快速回复 返回顶部 返回列表