平常我们用微信都是使用安卓客户端或者,IOS客户端,或者网页版,但是作为一个Programmer,必须得有点Programmer的亚子。 下载链接
在阅读之前请确保你有Javascript的基础,和Http相关的基础。
本文只分析微信发送消息(文本),不涉及其他微信其他接口的分析。
其参数有两部分,第一部分是QueryStringParameter里面的pass_ticket,登陆之后就是固定值。
第二部分是请求体中的部分:
DeviceId:是按照当前时间随机生成的生成规则如下:
"e" + ("" + Math.random().toFixed(15)).substring(2, 17) 复制代码
Sid是在Cookie中的:
getSid: function() { return n || (n = a.getCookie("wxsid")) }, 复制代码
Skey:固定值(服务端传回来的,同一账号每次登陆都是如此)
Uin:固定值(服务端传回来的,同一账号每次登陆都是如此)
ClentMsgId和LocalId:通过当前时间戳生成:
//utilFactory.now()等同于Date.now() e.ClientMsgId = e.LocalID = e.MsgId = (utilFactory.now() + Math.random().toFixed(3)).replace(".", "") 复制代码
Content:消息内容
FromUserName:自身账号的标识(每次登陆都会不同)
ToUserName:消息接收用户的标识(每次登陆都会不同)
Type:消息类型
成功的示例包含:
Ok,如果我们能获取到如上的返回结果就基本确认可以发出去了。
此次使用Java进行测试,并在下面贴出了代码。其他语言想测试的话,可参考思路。
import org.jsoup.Connection; import org.jsoup.Jsoup; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.HttpURLConnection; import java.net.URL; import java.text.NumberFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; public class Ts { public static void main(String[] args) throws IOException { WX wx = new WX(); HashMap<String, String> map = new HashMap<>(); map.put("passTicket", "xc5t%252FDlS9Rwo47Fn9etDMuF7RwmY3g9eFQmRVuioZAudH3T8EuAYXQB8MhdywQRd"); map.put("cookie", "wxuin=1581772621; webwxuvid=41eb93e6ef2e38a577ea2ea6b903594c88846cca31e6dda901afa2f0f2681cd59a0c8dacb47291f8e654bdc71f66e5bd; last_wxuin=1581772621; pgv_pvi=540184576; pgv_pvid=5488108125; ptui_loginuin=228846384; RK=RZKt0BQJZO; ptcz=b1c17e858051a1a4b4ae7af9bbda73303db615c3aa263dc5d0d37e2788187d0f; mm_lang=zh_CN; MM_WX_NOTIFY_STATE=1; MM_WX_SOUND_STATE=1; wxsid=F4EYzJ9vuXu0ruhm; webwx_data_ticket=gSc4Xg3VXZl/lJGgCqpviUOv; webwx_auth_ticket=CIsBEMjWxBEagAF3bcXg4+deLUBcXm7QCmJlYQa4NzMYR+J8Mo1vx06AorpEG4CqhlYzsKxfJTWUIxcX0uNifVtbz/5MTQGkhu/ZG3oAQfEqqMN8aj/RPMWUFSDk0YwcnniAVH1joTTq3m/Znp2WGmL8zpZQoOkbhWx8mRsYH07Ln7EKExxiNaLwfw==; login_frequency=2; wxloadtime=1562203882_expired; wxpluginkey=1562197398"); String fromUser = "@38a4bd3020c368f82d7cf6183f545bb2b8aa0096995c06c9ebfedaac8729cdc5"; String toUser = "@52e7190ff0ab4006f5199fda9a47def0cd5815d437ded69bd75d3d44551b938e"; wx.sendMsg("你好", map, fromUser, toUser); } } 复制代码
我们看到了确实成功了。
import org.jsoup.Connection; import org.jsoup.Jsoup; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStream; import java.math.BigDecimal; import java.math.RoundingMode; import java.net.HttpURLConnection; import java.net.URL; import java.text.NumberFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; public class WX { public static String sendMsg(String msg, Map<String, String> map, String fromUser, String toUser) throws IOException { BigDecimal b = new BigDecimal(Math.random()); double v = b.setScale(15, BigDecimal.ROUND_HALF_UP).doubleValue(); String dId = "e" + ("" + v).substring(2, 17); String timeStamp = getTimeStamp(); String param = "{/n" + " /"BaseRequest/": {/n" + " /"Uin/": 1581772621,/n" + " /"Sid/": /"F4EYzJ9vuXu0ruhm/",/n" + " /"Skey/": /"@crypt_94354c03_6bff423a698bb93e83549fd90906c2d6/",/n" + " /"DeviceID/": /"" + dId + "/"/n" + " },/n" + " /"Msg/": {/n" + " /"Type/": 1,/n" + " /"Content/": /"" + msg + "/",/n" + " /"FromUserName/": /"" + fromUser + "/",/n" + " /"ToUserName/": /"" + toUser + "/",/n" + " /"LocalID/": /"" + timeStamp + "/",/n" + " /"ClientMsgId/": /"" + timeStamp + "/"/n" + " },/n" + " /"Scene/": 0/n" + "}"; System.out.println(param); System.out.println(dId); URL url = new URL("https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket=" + map.get("passTicket")); HttpURLConnection urlconn = (HttpURLConnection) url.openConnection(); urlconn.setRequestMethod("POST"); urlconn.setDoInput(true); urlconn.setDoOutput(true); urlconn.setRequestProperty("userAgent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36"); urlconn.setRequestProperty("referrer", "https://wx2.qq.com/?⟨=zh_CN"); urlconn.setRequestProperty("Accept", "application/json, text/plain, */*"); urlconn.setRequestProperty("Accept-Encoding", "gzip, deflate, br"); urlconn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8"); urlconn.setRequestProperty("Connection", "keep-alive"); urlconn.setRequestProperty("Cookie", map.get("cookie")); urlconn.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); urlconn.setRequestProperty("Host", "wx.qq.com"); OutputStream outputStream = urlconn.getOutputStream(); outputStream.write(param.getBytes()); outputStream.flush(); int responseCode = urlconn.getResponseCode(); if (responseCode != 200) { throw new RuntimeException("Failed : HTTP error code : " + urlconn.getResponseCode()); } BufferedReader responseBuffer = new BufferedReader(new InputStreamReader( (urlconn.getInputStream()), "UTF-8")); String output = ""; StringBuffer stringBuffer = new StringBuffer(); System.out.println("Output from Server:/n"); while ((output = responseBuffer.readLine()) != null) { stringBuffer.append(output); System.out.println(output); } urlconn.disconnect(); return stringBuffer.toString(); } private static String getTimeStamp() { NumberFormat nf = NumberFormat.getNumberInstance(); // 保留两位小数 nf.setMaximumFractionDigits(3); // 如果不需要四舍五入,可以使用RoundingMode.DOWN nf.setRoundingMode(RoundingMode.UP); return (new Date().getTime() + "" + nf.format(Math.random())).replace(".", ""); } } 复制代码
关于chrome调试的问题,下次文章会发出来,敬请期待。