★我要吧★

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

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

[复制链接]
发表于 2016-2-14 09:06:30 | 显示全部楼层 |阅读模式
昨天是周末,在家闲得无聊,于是去weiphone逛了一圈,偶然发现有人发了一帖叫《微信 for Mac》,这勾起了我的好奇心,国内做Mac开发的人确实很少,对于那些能够独自开发一些Mac第三方工具的开发者我都表示很敬畏,于是点进去看了一个究竟,如果你们好奇也可以点进去看个明白,我最终得出的结论就是:坑爹呢这是!直接用一个WebView去加载了wx.qq.com这个网页也敢自称是微信For Mac?对于这种欺骗用户的行为我十分不屑,同时也让我在思考在微信不提供API的环境下开发一款原生的微信Mac版本是否可行,最有可能的就是去分析微信Web版本的通信过程,然后在程序中模拟这个流程,在我苦苦研究了一个下午之后,终于摸透了这个过程,并用程序实现了大部分功能,下面就详细解说一下微信Web版的流程:
  X5 b5 q' c  d$ `6 h2 b  T( P9 C
% i; M0 d8 j' K0 ?1.微信服务器返回一个会话ID
$ h2 X) b" |* n/ p  b4 g) X  r! N/ F2 |5 L
微信Web版本不使用用户名和密码登录,而是采用二维码登录,所以服务器需要首先分配一个唯一的会话ID,用来标识当前的一次登录,通过请求地址:
/ t/ [3 X, `6 q" H+ U# r
% C' @" r6 _' ~" W9 \! mhttps://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这个值是当前距离林威治标准时间的毫秒)
# c3 W. G( W$ y6 b* ]0 k
4 }1 e7 [( v2 X. Y# x' u服务器会返回如下的字符串:2 i! I& S- x/ S6 h5 e! T" F
1 F! ~: B7 j. ^" T9 R( b
window.QRLogin.code = 200; window.QRLogin.uuid = “DeA6idundY9VKn”;; {0 f/ R5 `; d
" u- C0 v& u2 Q# V
而这个DeA6idundY9VKn字符串就是微信服务器返回给我们的ID。
: x6 _$ g, S8 p* q0 {- w6 ^$ f
" I; j& G; L, `' s; u& m* K2.通过会话ID获得二维码
/ \  K% Y# m# ?0 I1 i
: z# U2 M  f1 {; x+ L2 E- z; k% x1 f既然微信Web版本是通过二维码进行登录,如何获得这个随机的二维码呢?答案就是利用刚才获得的ID去请求服务器生成的二维码,通过上面的ID我们组合得到以下的URL地址:  A0 t( l* ?7 k. S9 F6 L, z; q

; d* l7 K( x. e# t) y# Nhttps://login.weixin.qq.com/qrcode/DeA6idundY9VKn?t=webwx
" M6 K2 `9 B; D4 f* N1 j6 z: }/ R6 P* M! a- ^; e
该请求返回的便是我们需要的二维码,此时需要用户在微信的手机版本中扫描这个二维码(我就搞不明白微信官方是如何想的,登录Web版本竟然还需要手机微信去配合登录,难道没有考虑我被迫选择Web微信就是因为手机不在身边这样的情形么?)
; s, b0 v, t' A- p* V* }4 z6 I. R  B. t) h: m- {$ o" ~& z# K. i' U
3.轮询手机端是否已经扫描二维码并确认在Web端登录/ V5 Q4 Z- v, ?; W6 a- n
1 L- N1 ]5 U8 A: B: G
当获得二维码之后,就需要用户去手机端去扫描二维码,并获得用户的授权,此时我们并不知道用户何时完成这个操作,所以我们只有轮询,而轮询的地址就是:
- N2 g( U" `8 I( W* |; P  I
' q* k7 M/ C, e4 {* O2 Khttps://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid=DeA6idundY9VKn&tip=1&_=1377482045264(注意UUID和最后时间这两个参数)5 D- r% }  E* G% _

0 N4 z, t% e: R2 q' y2 K如果服务器返回:
5 _' K7 b9 J+ F' m2 X+ n
3 |  q" U% U7 }# @3 L, Kwindow.code=201;
! q6 }7 Y& H" L, F5 n9 N
6 c) e6 g5 q9 U) v7 x7 A则说明此时用户在手机端已经完成扫描,但还没有点击确认;! w* B" T& a% e) O4 {# k# N

  B% G, T* R( f+ r. T如果服务器返回:0 |3 p/ w5 [- ^4 Y. J' m
8 g' M% t% D$ o
window.redirect_uri=一个URL地址
0 x3 v3 z$ Z/ M2 r
) O' v0 }4 p: J2 ]4 a则说明此时用户已经在手机端完成了授权过程,保存下这个URL地址下一步骤中使用。! q  Q$ }" J* N6 A
" y5 Q$ z1 x: C" ~  _. t; x
4.访问登录地址,获得uin和sid/ x; W" W! m8 q7 z: q8 H

$ |- m8 ^7 z+ s2 \2 |; c通过访问上一步骤中获得的URL地址,可以在服务器返回的Cookies中获得到wxuin和wxsid这两个值,这两值在后续的通信过程中都要使用到这两个值,并且Cookies中也需要包括这两项。4 y( r5 m/ v6 @9 ^
% h7 N8 o' n" S5 R/ ^6 h8 Q
5.初使化微信信息
! m' h" J, }# r1 w9 C: `2 P: I' {
前面的步骤算是完成了这个复杂的登录过程,如果我们需要使用微信就需要获得当前用户的信息、好友列表等,还有一个关键的就是同步信息(后续与服务器轮询中需要使用同步信息),通过访问以下的链接:
, F# `& l6 ?# ~, @3 [7 {' |# r( E2 [: w6 L4 H
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=1377482058764(r依然是时间). D- q* Y. T: Y$ P+ D. C
  p! l! ?+ X2 G" o1 A
访问该链接需要使用POST,并且在Body中带上以下的JSON信息:
' h% u3 J' J7 W: i, h6 u' S& M/ |7 c% T8 j% y  [9 P
1
4 r3 n9 `+ d, h2 ~3 y: C2
( e7 l: z3 a; d, e( W& {& r) d/ c{"BaseRequest":
! h0 @! q7 A- h, h' b4 L{"Uin":"2545437902","Sid":"QfLp+Z+FePzvOFoG","Skey":"","DeviceID":"e1615250492"}}9 q" v0 ^- W: K. E2 ]+ E# V6 V
这个JSON串中Uin和Sid分别是上面步骤中获得的那两个Cookie值,DeviceID是一个本地生成的随机字符串(分析了官方的总是e+一串数字,所以我们也保持这样的格式)。3 `) `$ t3 c. j0 ~5 g
! E- I5 X7 ?4 k% b# Y) _
服务器就会返回一个很长的JSON串,这其中包括:BaseResponse中的值用来表示请求状态码,ContactList主要用来表示联系人(此列表不全,只包括了类似通讯录助手、文件助手、微信团队和一些公众帐号等,后面会通过另一接口去获得更全面的信息),SyncKey是用户与服务器同步的信息,User就是当前登录用户自己的信息。& E0 {/ I- d% J$ ^+ \9 @1 }
) l) X8 X1 i2 \: P
6.获得所有的好友列表/ T, Y  b- b2 Q6 R8 x
5 Q# i! f- }7 L8 v3 }& x
在上一步骤中已经获得了部分好友和公众帐号,如果需要获得完整的好友信息,就需要访问以下的链接:
7 j: O# s' }2 g- Z2 [) I4 s$ O$ o
: J# K% L* M) r1 X  |https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?r=1377482079876(r依然是时间)8 \) Z8 I1 [  u" b1 t
7 g* F/ A% [" d" ^* j
访问该链接同样需要POST方式,但Body为空JSON:{},服务器对身份的判定是通过Cookies,所以需要保持之前访问的Cookies不被修改(在Objective-C中会自动保存相关的Cookies,无需程序特殊处理),在返回的JSON串中,MemberList中就包含了所有的好友信息。
: ?5 ^+ ^* V7 Z5 R+ v5 Z. l+ y# y  P0 Q; C0 \
7.保持与服务器的信息同步$ N  J2 A1 r/ _+ W7 O' U% f8 C
$ U' |3 F. P5 |/ N$ D: Q9 o4 L& i& K
与服务器保持同步需要在客户端做轮询,该轮询的URL如下:6 @- W" W! a6 W8 t8 X- c

/ ~% {& X" `! Jhttps://webpush.weixin.qq.com/cgi-bin/mmwebwx-bin/synccheck?callback=jQuery18309326978388708085_1377482079946&r=1377482079876&
- ^4 h, q! n; `- R  k2 Lsid=QfLp+Z+FePzvOFoG&uin=2545437902&deviceid=e1615250492&synckey=(见以下说明)&_=13774820798760 ?# C8 p3 H) K. T

$ K7 q( v% L7 w其中的参数r和_都是time,sid,uin,deviceid与上面步骤的值相对应,此处的synkey是上步步骤获得的同步键值,但需要按一定的规则组合成以下的字符串:
8 z0 t% a3 G5 O5 R0 f6 G4 P# r4 |
1_124125|2_452346345|3_65476547|1000_56436354 n3 N6 n' [2 a. E' a; B7 o
3 L( L* e/ }9 a: S& L$ D
就是将键和值用_隔开,不同的键值对用|隔开,但记得|需要URL编码成%7C,通过访问上面的地址,会返回如下的字符串:
5 s1 Q% a! E5 x; X+ k+ k' p6 t& ?( j* ]/ h
window.synccheck={retcode:”0”,selector:”0”}
! _' z2 `8 m1 ~3 `/ _1 l
# J2 C+ h) p7 {; M如果retcode中的值不为0,则说明与服务器的通信有问题了,但具体问题我就无法预测了,selector中的值表示客户端需要作出的处理,目前已经知道当为6的时候表示有消息来了,就需要去访问另一个接口获得新的消息。
) w) C3 b( ^$ B6 f) |$ |0 |/ |2 a5 D2 D* H8 {% a0 S
8.获得别人发来的消息0 O; N1 i0 r8 k% z* f0 I( G+ ]- b
5 f" |+ J4 C; n( Q0 w6 _3 B
当一个步骤中知道有新消息时,就需要去获取消息内容,通过访问以下的链接:
' v* F( A$ b. Y5 N/ X* R- ^, h; Y* z
https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=QfLp+Z+FePzvOFoG&r=1377482079876  F4 h4 R* V8 O0 ~
- t2 l1 U8 l  B0 z7 y  |+ ~% ?
上面链接中的参数sid对应上面步骤中的值,r为时间,访问链接需要使用POST方式,Body中包括JSON串,该JSON串格式如下:0 C2 }& R2 _0 W# m7 q
% S& _: b2 E5 H/ Q6 Z& k
1+ I+ Y" n; [0 m' [& y3 z( ^3 O
2
5 V1 G9 z/ a0 c& U1 Z* E34 V1 F. t2 |) {* P1 V
{"BaseRequest" : {"Uin":2545437902,"Sid":"QfLp+Z+FePzvOFoG"},9 K; @9 r  P, |( C
"SyncKey" : {"Count":4,"List":[{"Key":1,"Val":620310295},{"Key":2,"Val":620310303},{"Key":3,"Val":620310285},{"Key":1000,"Val":1377479086}]},+ l- b- f% w& ]5 t8 F
"rr" :1377482079876};
0 q5 h, Q8 l& c) S7 _+ M以下的信息中BaseRequest中包括的Uin与Sid与上面步骤中的值对应,SyncKey也是上面步骤中获得的同步键值对,rr为时间,访问成功之后服务器会返回一个JSON串,其中AddMsgList中是一个数组,包含了所有新消息。
. P3 `* g, K- y" f; I
) I9 c: k* Z( K8 \5 E7 q& d9.向用户发送消息$ `3 \  }# @4 k$ R# p
- ^) P. q4 a) R, S/ A& O
用户主动发送消息,通过以下的URL地址:
" R) Q1 O/ s* `# L5 \1 Thttps://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?sid=QfLp+Z+FePzvOFoG&r=13774820798767 O. X" l# |  l% b- e
上面的sid和r参数不再解释了,访问该URL采用POST方式,在Body中的JSON串形如以下的格式:" n% L/ g4 H* L8 a1 ^
3 {% \5 y/ N# Q- m
1
! C8 s* T$ F" r9 G28 L& C% C7 {& E
3( F3 \5 H2 ~7 u, ]
4
" o5 C, `& @  I- @! o5
5 V( y: K! q/ n; y& }' u# l- i6
5 }8 q7 k9 o1 q6 \3 p7% X8 A' @: T0 C* l
8+ w8 J. O0 n7 a/ F0 `
9
6 d4 I5 }2 E" k- \10
0 N: `( h9 V5 \( P2 \% A114 p+ w' F. n4 @9 L
12
' x( J+ N' l" j& w; \13
' J7 ~- P  I1 t' H) Q3 u14
& u. L$ b+ o7 P/ `& Q  E2 I15
; m$ n0 j( U4 N  g3 m16
4 H# G3 ^7 O& U5 U. J17
- D. b3 C  O2 c9 z4 Z- [, E{
, {" C  p3 v: b    "BaseRequest":{
% `* b- D  j' @        "DeviceID" : "e441551176",
: e6 K/ U( T1 l* I        "Sid" : "S8wNi91Zry3024eg",
$ e! o+ w0 P7 \& j. v        "Skey" : "F820928BBA5D8ECA23448F076D2E8A915E1349E9FB4F4332",
$ A! K. B* {1 M9 u0 h- }* y, M; I        "Uin" : "2545437902"
. v: t8 l  L5 D' c$ C    },+ z; R* p: s; d  F3 G
    "Msg" : {3 a0 ?6 \1 b. B1 Z1 g
        "ClientMsgId" : 1377504862158,
$ \) n3 s1 w6 I% n6 ^9 z- e        "Content" : "hello",
9 F- k- Z  E8 Z4 I6 x4 l        "FromUserName" : "wxid_2rrz8g8ezuox22",# @" n$ X5 n: m' K9 s, Z
        "LocalID" : 1377504862158,
. I+ M* ^6 x' g3 J3 ?' j+ n& u        "ToUserName" : "wxid_j4nu420ojhsr21",
1 D& x! ^1 }  x' R! j  `        "Type" : 1
, y7 S  t3 A" N    },
8 l" s; s0 c( M4 O    "rr" = 1377504864463
% T% i' S7 n$ x/ c: Z}
( Z  Q% h  G4 b8 t其中BaseRequest都是授权相关的值,与上面的步骤中的值对应,Msg是对消息的描述,包括了发送人与接收人,消息内容,消息的类型(1为文本),ClientMsgId和LocalID由本地生成。rr可用当前的时间。
; W% V! {3 B5 d2 M在返回JSON结果中BaseResponse描述了发送情况,Ret为0表示发送成功。
  G( T& E" }, z2 n/ ~' j* @8 V) b; j
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

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

GMT+8, 2024-11-24 21:49 , Processed in 0.092493 second(s), 18 queries .

Powered by abc369 X3.4

© 2001-2023 abc369.

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