From 342a8ed5c9e17039b9776fc49b3cce17c33bcaf7 Mon Sep 17 00:00:00 2001 From: hotlcc Date: Wed, 25 Jul 2018 20:33:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/hotlcc/wechat4j/Wechat.java | 142 +++++++++++------- .../com/hotlcc/wechat4j/api/WebWeixinApi.java | 33 +++- .../resources/config/webwx-url.properties | 24 +-- 3 files changed, 132 insertions(+), 67 deletions(-) diff --git a/src/main/java/com/hotlcc/wechat4j/Wechat.java b/src/main/java/com/hotlcc/wechat4j/Wechat.java index 7db535b..4f2ba46 100644 --- a/src/main/java/com/hotlcc/wechat4j/Wechat.java +++ b/src/main/java/com/hotlcc/wechat4j/Wechat.java @@ -55,19 +55,21 @@ public class Wechat { private volatile String passTicket; private volatile String skey; private volatile String wxuin; + //url版本号 + private volatile String urlVersion; //用户数据 private volatile UserInfo loginUser; private final Lock loginUserLock = new ReentrantLock(); - private volatile JSONObject SyncKey; - private final Lock SyncKeyLock = new ReentrantLock(); - private volatile List ContactList; - private final Lock ContactListLock = new ReentrantLock(); + private volatile JSONObject syncKey; + private final Lock syncKeyLock = new ReentrantLock(); + private volatile List contactList; + private final Lock contactListLock = new ReentrantLock(); //在线状态 private volatile boolean isOnline = false; private final Lock isOnlineLock = new ReentrantLock(); + //同步监听器 private volatile SyncMonitor syncMonitor; - //退出事件处理器 private List exitEventHandlers; //接收消息处理器 @@ -274,13 +276,12 @@ public class Wechat { * * @return */ - private String waitForConfirm(PrintStream ps, String uuid) { + private JSONObject waitForConfirm(PrintStream ps, String uuid) { ps.print("等待手机端扫码..."); ps.flush(); - String code = null; boolean flag = false; - while (!"200".equals(code)) { + while (true) { JSONObject result = webWeixinApi.getRedirectUri(httpClient, LoginTipEnum.TIP_0, uuid); if (result == null) { ps.println("\t失败:出现异常"); @@ -288,7 +289,7 @@ public class Wechat { return null; } - code = result.getString("code"); + String code = result.getString("code"); if ("408".equals(code)) { ps.print("."); ps.flush(); @@ -306,15 +307,13 @@ public class Wechat { } continue; } else if ("200".equals(code)) { - String redirectUri = result.getString("redirectUri"); ps.println("\t成功,认证完成"); ps.flush(); - return redirectUri; + return result; } else { return null; } } - return null; } /** @@ -360,7 +359,7 @@ public class Wechat { ps.print("尝试push方式获取uuid..."); ps.flush(); - JSONObject result = webWeixinApi.pushLogin(httpClient, wxuin); + JSONObject result = webWeixinApi.pushLogin(httpClient, urlVersion, wxuin); if (result == null) { ps.println("\t失败:出现异常"); ps.flush(); @@ -393,7 +392,7 @@ public class Wechat { * @return */ private boolean wxInit() { - JSONObject result = webWeixinApi.webWeixinInit(httpClient, passTicket, new BaseRequest(wxsid, skey, wxuin)); + JSONObject result = webWeixinApi.webWeixinInit(httpClient, urlVersion, passTicket, new BaseRequest(wxsid, skey, wxuin)); if (result == null) { return false; } @@ -409,7 +408,7 @@ public class Wechat { } loginUser = UserInfo.valueOf(result.getJSONObject("User")); - SyncKey = result.getJSONObject("SyncKey"); + syncKey = result.getJSONObject("SyncKey"); return true; } @@ -457,7 +456,7 @@ public class Wechat { */ private boolean statusNotify(int time) { for (int i = 0; i < time; i++) { - JSONObject result = webWeixinApi.statusNotify(httpClient, passTicket, new BaseRequest(wxsid, skey, wxuin), getLoginUserName(false)); + JSONObject result = webWeixinApi.statusNotify(httpClient, urlVersion, passTicket, new BaseRequest(wxsid, skey, wxuin), getLoginUserName(false)); if (result == null) { continue; } @@ -481,7 +480,7 @@ public class Wechat { /** * 自动登录 */ - public boolean autoLogin(OutputStream os) { + public boolean autoLogin(OutputStream os, boolean tryPushLogin) { // 0、获取消息打印流 PrintStream ps = null; if (os != null) { @@ -502,7 +501,7 @@ public class Wechat { // 2、登录 // 2.1、获取uuid String uuid = null; - if (StringUtil.isNotEmpty(wxuin)) { + if (tryPushLogin && StringUtil.isNotEmpty(wxuin)) { uuid = getWxUuid(ps, wxuin); } if (StringUtil.isEmpty(uuid)) { @@ -520,14 +519,15 @@ public class Wechat { return false; } // 2.3、等待确认 - String redirectUri = waitForConfirm(ps, uuid); - if (StringUtil.isEmpty(redirectUri)) { + result = waitForConfirm(ps, uuid); + if (result == null) { ps.println("手机端认证失败,登录不成功"); ps.flush(); return false; } + urlVersion = result.getString("urlVersion"); // 2.4、获取登录认证码 - if (!getLoginCode(ps, redirectUri)) { + if (!getLoginCode(ps, result.getString("redirectUri"))) { ps.println("无法获取登录认证码,登录不成功"); ps.flush(); return false; @@ -562,23 +562,57 @@ public class Wechat { * @return */ public boolean autoLogin() { - return autoLogin(null); + return autoLogin(null, false); } /** * 退出登录 */ - public void logout() { + public void logout(boolean clearAllLoginInfo) { try { isOnlineLock.lock(); - webWeixinApi.logout(httpClient, new BaseRequest(wxsid, skey, wxuin)); + webWeixinApi.logout(httpClient, urlVersion, new BaseRequest(wxsid, skey, wxuin)); isOnline = false; + + if (clearAllLoginInfo) { + clearAllLoginInfo(); + } } finally { isOnlineLock.unlock(); } } + public void logout() { + logout(true); + } + + /** + * 清除全部登录信息 + */ + private void clearAllLoginInfo() { + try { + loginUserLock.lock(); + syncKeyLock.lock(); + contactListLock.lock(); + + wxsid = null; + passTicket = null; + skey = null; + urlVersion = null; + loginUser = null; + syncKey = null; + if (contactList != null) { + contactList.clear(); + contactList = null; + } + } finally { + loginUserLock.unlock(); + syncKeyLock.unlock(); + contactListLock.unlock(); + } + } + /** * 判断在线状态 * @@ -607,7 +641,7 @@ public class Wechat { try { //API调用异常导致退出 - JSONObject result = webWeixinApi.syncCheck(httpClient, new BaseRequest(wxsid, skey, wxuin), getSyncKeyList(false)); + JSONObject result = webWeixinApi.syncCheck(httpClient, urlVersion, new BaseRequest(wxsid, skey, wxuin), getSyncKeyList(false)); logger.debug("微信同步监听心跳返回数据:{}", result); if (result == null) { throw new RuntimeException("微信API调用异常"); @@ -738,7 +772,7 @@ public class Wechat { */ private void webWxSync() { try { - JSONObject result = webWeixinApi.webWxSync(httpClient, passTicket, new BaseRequest(wxsid, skey, wxuin), SyncKey); + JSONObject result = webWeixinApi.webWxSync(httpClient, urlVersion, passTicket, new BaseRequest(wxsid, skey, wxuin), syncKey); if (result == null) { logger.error("从服务端同步新数据异常"); return; @@ -762,10 +796,10 @@ public class Wechat { //更新SyncKey try { - SyncKeyLock.lock(); - SyncKey = result.getJSONObject("SyncKey"); + syncKeyLock.lock(); + syncKey = result.getJSONObject("SyncKey"); } finally { - SyncKeyLock.unlock(); + syncKeyLock.unlock(); } } catch (Exception e) { logger.error("Execute webWxSync error.", e); @@ -834,7 +868,7 @@ public class Wechat { */ public UserInfo getLoginUser(boolean update) { if (loginUser == null || update) { - JSONObject result = webWeixinApi.webWeixinInit(httpClient, passTicket, new BaseRequest(wxsid, skey, wxuin)); + JSONObject result = webWeixinApi.webWeixinInit(httpClient, urlVersion, passTicket, new BaseRequest(wxsid, skey, wxuin)); if (result == null) { return loginUser; } @@ -896,34 +930,34 @@ public class Wechat { * @return */ private JSONObject getSyncKey(boolean update) { - if (SyncKey == null || update) { - JSONObject result = webWeixinApi.webWeixinInit(httpClient, passTicket, new BaseRequest(wxsid, skey, wxuin)); + if (syncKey == null || update) { + JSONObject result = webWeixinApi.webWeixinInit(httpClient, urlVersion, passTicket, new BaseRequest(wxsid, skey, wxuin)); if (result == null) { - return SyncKey; + return syncKey; } JSONObject BaseResponse = result.getJSONObject("BaseResponse"); if (result == null) { - return SyncKey; + return syncKey; } int Ret = BaseResponse.getIntValue("Ret"); if (Ret != 0) { - return SyncKey; + return syncKey; } try { - SyncKeyLock.lock(); - if (SyncKey == null || update) { - SyncKey = result.getJSONObject("SyncKey"); + syncKeyLock.lock(); + if (syncKey == null || update) { + syncKey = result.getJSONObject("SyncKey"); } } finally { - SyncKeyLock.unlock(); + syncKeyLock.unlock(); } - return SyncKey; + return syncKey; } - return SyncKey; + return syncKey; } /** @@ -947,34 +981,34 @@ public class Wechat { * @return */ public List getContactList(boolean update) { - if (ContactList == null || update) { - JSONObject result = webWeixinApi.getContact(httpClient, passTicket, skey); + if (contactList == null || update) { + JSONObject result = webWeixinApi.getContact(httpClient, urlVersion, passTicket, skey); if (result == null) { - return ContactList; + return contactList; } JSONObject BaseResponse = result.getJSONObject("BaseResponse"); if (BaseResponse == null) { - return ContactList; + return contactList; } String Ret = BaseResponse.getString("Ret"); if (!"0".equals(Ret)) { - return ContactList; + return contactList; } try { - ContactListLock.lock(); - if (ContactList == null || update) { - ContactList = UserInfo.valueOf(result.getJSONArray("MemberList")); + contactListLock.lock(); + if (contactList == null || update) { + contactList = UserInfo.valueOf(result.getJSONArray("MemberList")); } } finally { - ContactListLock.unlock(); + contactListLock.unlock(); } - return ContactList; + return contactList; } - return ContactList; + return contactList; } /** @@ -1089,7 +1123,7 @@ public class Wechat { } message.setType(MsgTypeEnum.TEXT_MSG.getCode()); - JSONObject result = webWeixinApi.sendMsg(httpClient, passTicket, baseRequest, message); + JSONObject result = webWeixinApi.sendMsg(httpClient, urlVersion, passTicket, baseRequest, message); return result; } @@ -1152,7 +1186,7 @@ public class Wechat { BaseRequest baseRequest = new BaseRequest(wxsid, skey, wxuin); String dataTicket = getCookieValue("webwx_data_ticket"); - JSONObject result = webWeixinApi.uploadMedia(httpClient, passTicket, baseRequest, loginUserName, toUserName, dataTicket, image); + JSONObject result = webWeixinApi.uploadMedia(httpClient, urlVersion, passTicket, baseRequest, loginUserName, toUserName, dataTicket, image); return result; } } diff --git a/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java b/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java index 7b2390c..52dccee 100644 --- a/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java +++ b/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java @@ -48,6 +48,7 @@ public class WebWeixinApi { private static Pattern PATTERN_UUID_2 = Pattern.compile("window.QRLogin.code = (\\d+); window.QRLogin.uuid = \"(\\S+?)\";"); private static Pattern PATTERN_REDIRECT_URI_1 = Pattern.compile("window.code=(\\d+);"); private static Pattern PATTERN_REDIRECT_URI_2 = Pattern.compile("window.code=(\\d+);\\s*window.redirect_uri=\"(\\S+?)\";"); + private static Pattern PATTERN_REDIRECT_URI_3 = Pattern.compile("http(s*)://wx(\\d*)\\.qq\\.com\\/"); /** * 获取微信uuid @@ -184,8 +185,16 @@ public class WebWeixinApi { if (!matcher.find()) { throw new RuntimeException("没有匹配到跳转uri"); } + String redirectUri = matcher.group(2); result.put("msg", "手机确认成功"); - result.put("redirectUri", matcher.group(2)); + result.put("redirectUri", redirectUri); + + matcher = PATTERN_REDIRECT_URI_3.matcher(redirectUri); + if (!matcher.find()) { + throw new RuntimeException("从跳转uri中没有匹配到url版本号"); + } + String urlVersion = matcher.group(2); + result.put("urlVersion", urlVersion); } else { throw new RuntimeException("返回code错误"); } @@ -233,6 +242,7 @@ public class WebWeixinApi { * 退出登录 */ public void logout(HttpClient httpClient, + String urlVersion, BaseRequest BaseRequest) { try { List pairList = new ArrayList<>(); @@ -242,6 +252,7 @@ public class WebWeixinApi { //分两步进行 for (int i = 0; i <= 1; i++) { String url = new ST(PropertiesUtil.getProperty("webwx-url.logout_url")) + .add("urlVersion", urlVersion) .add("type", i) .add("skey", StringUtil.encodeURL(BaseRequest.getSkey(), Consts.UTF_8.name())) .render(); @@ -263,10 +274,12 @@ public class WebWeixinApi { * push登录 */ public JSONObject pushLogin(HttpClient httpClient, + String urlVersion, String wxuin) { try { long millis = System.currentTimeMillis(); String url = new ST(PropertiesUtil.getProperty("webwx-url.pushlogin_url")) + .add("urlVersion", urlVersion) .add("uin", wxuin) .render(); @@ -293,10 +306,12 @@ public class WebWeixinApi { * 获取初始化数据 */ public JSONObject webWeixinInit(HttpClient httpClient, + String urlVersion, String passticket, BaseRequest BaseRequest) { try { String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxinit_url")) + .add("urlVersion", urlVersion) .add("pass_ticket", passticket) .add("r", System.currentTimeMillis() / 1252L) .render(); @@ -331,11 +346,13 @@ public class WebWeixinApi { * @return */ public JSONObject statusNotify(HttpClient httpClient, + String urlVersion, String passticket, BaseRequest BaseRequest, String loginUserName) { try { String url = new ST(PropertiesUtil.getProperty("webwx-url.statusnotify_url")) + .add("urlVersion", urlVersion) .add("pass_ticket", passticket) .render(); @@ -371,11 +388,13 @@ public class WebWeixinApi { * 服务端状态同步心跳 */ public JSONObject syncCheck(HttpClient httpClient, + String urlVersion, BaseRequest BaseRequest, JSONArray SyncKeyList) { try { long millis = System.currentTimeMillis(); String url = new ST(PropertiesUtil.getProperty("webwx-url.synccheck_url")) + .add("urlVersion", urlVersion) .add("r", millis) .add("skey", StringUtil.encodeURL(BaseRequest.getSkey(), Consts.UTF_8.name())) .add("sid", BaseRequest.getSid()) @@ -421,10 +440,12 @@ public class WebWeixinApi { * 获取全部联系人列表 */ public JSONObject getContact(HttpClient httpClient, + String urlVersion, String passticket, String skey) { try { String url = new ST(PropertiesUtil.getProperty("webwx-url.getcontact_url")) + .add("urlVersion", urlVersion) .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name())) .add("r", System.currentTimeMillis()) .add("skey", StringUtil.encodeURL(skey, Consts.UTF_8.name())) @@ -452,11 +473,13 @@ public class WebWeixinApi { * 批量获取指定用户信息 */ public JSONObject batchGetContact(HttpClient httpClient, + String urlVersion, String passticket, BaseRequest BaseRequest, JSONArray batchContactList) { try { String url = new ST(PropertiesUtil.getProperty("webwx-url.batchgetcontact_url")) + .add("urlVersion", urlVersion) .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name())) .add("r", System.currentTimeMillis()) .render(); @@ -491,11 +514,13 @@ public class WebWeixinApi { * 从服务端同步新数据 */ public JSONObject webWxSync(HttpClient httpClient, + String urlVersion, String passticket, BaseRequest BaseRequest, JSONObject SyncKey) { try { String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxsync_url")) + .add("urlVersion", urlVersion) .add("skey", BaseRequest.getSkey()) .add("sid", BaseRequest.getSid()) .add("pass_ticket", passticket) @@ -530,11 +555,13 @@ public class WebWeixinApi { * 发送消息 */ public JSONObject sendMsg(HttpClient httpClient, + String urlVersion, String passticket, BaseRequest baseRequest, WxMessage message) { try { String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxsendmsg_url")) + .add("urlVersion", urlVersion) .add("pass_ticket", passticket) .render(); @@ -572,6 +599,7 @@ public class WebWeixinApi { * @return */ public JSONObject uploadMedia(HttpClient httpClient, + String urlVersion, String passticket, BaseRequest BaseRequest, String FromUserName, @@ -582,6 +610,7 @@ public class WebWeixinApi { ContentType contentType) { try { String url = new ST(PropertiesUtil.getProperty("webwx-url.uploadmedia_url")) + .add("urlVersion", urlVersion) .render(); HttpPost httpPost = new HttpPost(url); @@ -640,6 +669,7 @@ public class WebWeixinApi { * @return */ public JSONObject uploadMedia(HttpClient httpClient, + String urlVersion, String passticket, BaseRequest baseRequest, String fromUserName, @@ -648,6 +678,7 @@ public class WebWeixinApi { File file) { try { String url = new ST(PropertiesUtil.getProperty("webwx-url.uploadmedia_url")) + .add("urlVersion", urlVersion) .render(); HttpPost httpPost = new HttpPost(url); diff --git a/src/main/resources/config/webwx-url.properties b/src/main/resources/config/webwx-url.properties index 534484d..f3176ac 100644 --- a/src/main/resources/config/webwx-url.properties +++ b/src/main/resources/config/webwx-url.properties @@ -1,32 +1,32 @@ # 1、登录 ## 1.1、获取微信uuid -webwx-url.uuid_url=https://login.wx2.qq.com/jslogin?appid=&redirect_uri=https%3A%2F%2Fwx2.qq.com%2Fcgi-bin%2Fmmwebwx-bin%2Fwebwxnewloginpage&fun=new&lang=zh_CN&_=<_> +webwx-url.uuid_url=https://login.wx.qq.com/jslogin?appid=&fun=new&lang=zh_CN&_=<_> ## 1.2、获取二维码 webwx-url.qrcode_url=https://login.weixin.qq.com/qrcode/ ## 1.3、等待扫码登录并获取跳转url -webwx-url.redirect_uri=https://login.wx2.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=&tip=&r=&_=<_> +webwx-url.redirect_uri=https://login.wx.qq.com/cgi-bin/mmwebwx-bin/login?loginicon=true&uuid=&tip=&r=&_=<_> ## 1.4、获取登录认证码 webwx-url.newlogin_url=&fun=new&version=v2 ## 1.5、退出登录 -webwx-url.logout_url=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=&skey= +webwx-url.logout_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxlogout?redirect=1&type=&skey= ## 1.6、push登录 -webwx-url.pushlogin_url=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxpushloginurl?uin= +webwx-url.pushlogin_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxpushloginurl?uin= # 2、数据同步 ## 2.1、页面初始化 -webwx-url.webwxinit_url=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=&lang=zh_CN&pass_ticket= +webwx-url.webwxinit_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxinit?r=&lang=zh_CN&pass_ticket= ## 2.2、开启消息状态通知 -webwx-url.statusnotify_url=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify?lang=zh_CN&pass_ticket= +webwx-url.statusnotify_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxstatusnotify?lang=zh_CN&pass_ticket= ## 2.3、服务端状态同步 -webwx-url.synccheck_url=https://webpush.wx2.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=&skey=&sid=&uin=&deviceid=&synckey=&_=<_> +webwx-url.synccheck_url=https://webpush.wx.qq.com/cgi-bin/mmwebwx-bin/synccheck?r=&skey=&sid=&uin=&deviceid=&synckey=&_=<_> # 3、联系人管理 ## 3.1、获取全部联系人列表 -webwx-url.getcontact_url=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=&r=&seq=0&skey= +webwx-url.getcontact_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxgetcontact?lang=zh_CN&pass_ticket=&r=&seq=0&skey= ## 3.2、批量获取指定联系人列表 -webwx-url.batchgetcontact_url=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=&lang=zh_CN&pass_ticket= +webwx-url.batchgetcontact_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxbatchgetcontact?type=ex&r=&lang=zh_CN&pass_ticket= # 4、收发消息 ## 4.1、从服务端拉取新消息 -webwx-url.webwxsync_url=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=&skey=&pass_ticket= +webwx-url.webwxsync_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsync?sid=&skey=&pass_ticket= ## 4.2、发送消息 -webwx-url.webwxsendmsg_url=https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket= +webwx-url.webwxsendmsg_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket= ## 4.3、上传媒体文件 -webwx-url.uploadmedia_url=https://file.wx2.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json \ No newline at end of file +webwx-url.uploadmedia_url=https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json \ No newline at end of file