微信公眾號開發者模式介紹及接入
Java公眾號開發環境搭建
需要準備的東西:
一個微信公眾號,參考:微信公眾號申請及介紹
內網穿透工具,參考:使用natapp開啟內網穿透之旅
數據交互
編輯模式和開發模式的關系:
編輯模式和開發模式是互斥的關系,也就是說,當我們使用開發模式時,編輯模式下的操作就會失效。反之,使用編輯模式時,開發模式下的操作就會失效,所以只能使用其中一個模式進行公眾號的開發。
開發模式下,公眾號數據的交互流程:
注:圖中的微信公眾號服務器,就是我們開發者所要開發的部分
開發者模式接入
微信公眾平臺相關技術文檔地址如下:
微信公眾平臺技術文檔
入門指引
接入指南
我們根據 “接入指南” 中的說明來完成公眾平臺的接入,但是我們跳過文檔中的第一步,先來完成第二步的操作,即驗證消息的確來自微信服務器。因為提交服務器配置信息時微信會對配置的URL發起調用,驗證該服務器是否正??捎茫晕覀兊孟劝训诙酵瓿?,才能去完成第一步。既然是開發就得建工程了,所以在IDEA中創建一個SpringBoot工程,工程結構如下:
先說明一點:當我們提交服務器配置信息后,微信服務器將發送GET請求到填寫的服務器地址URL上,GET請求攜帶參數分別為signature、timestamp、nonce、echostr。開發者通過檢驗signature對請求進行校驗,若確認此次GET請求來自微信服務器,則原樣返回echostr參數內容,表示接入生效,成為開發者成功,否則接入失敗。加密/校驗流程如下:
1)將token、timestamp、nonce三個參數進行字典序排序
2)將三個參數字符串拼接成一個字符串進行SHA1加密
3)開發者獲得加密后的字符串可與signature對比,標識該請求來源于微信
可以看到,第二步中,我們需要將三個參數字符串拼接成一個字符串進行SHA1加密,這就涉及到SHA1加密算法。那么就需要一個專門的工具類來完成SHA1加密,所以需要在util包中,新建一個 SHA1Util 類,用于進行SHA1加密,代碼如下:
package?org.zero01.weixin.mqdemo.util;import?java.security.MessageDigest;/** ?*?@program:?mq-demo ?*?@description:?SHA1加密 ?*?@author:?01 ?*?@create:?2018-06-23?18:06 ?**/public?final?class?SHA1Util?{????private?static?final?char[]?HEX_DIGITS?=?{'0',?'1',?'2',?'3',?'4',?'5',????????????'6',?'7',?'8',?'9',?'a',?'b',?'c',?'d',?'e',?'f'};????/** ?????*?Takes?the?raw?bytes?from?the?digest?and?formats?them?correct. ?????* ?????*?@param?bytes?the?raw?bytes?from?the?digest. ?????*?@return?the?formatted?bytes. ?????*/ ????private?static?String?getFormattedText(byte[]?bytes)?{????????int?len?=?bytes.length; ????????StringBuilder?buf?=?new?StringBuilder(len?*?2);????????//?把密文轉換成十六進制的字符串形式 ????????for?(byte?aByte?:?bytes)?{ ????????????buf.append(HEX_DIGITS[(aByte?>>?4)?&?0x0f]); ????????????buf.append(HEX_DIGITS[aByte?&?0x0f]); ????????}????????return?buf.toString(); ????}????public?static?String?encode(String?str)?{????????if?(str?==?null)?{????????????return?null; ????????}????????try?{ ????????????MessageDigest?messageDigest?=?MessageDigest.getInstance("SHA1"); ????????????messageDigest.update(str.getBytes());????????????return?getFormattedText(messageDigest.digest()); ????????}?catch?(Exception?e)?{????????????throw?new?RuntimeException(e); ????????} ????} }
在util包中,再新建一個 WechatMqCheckedUtil 工具類,用于校驗微信發起調用時所傳遞的參數,代碼如下:
package?org.zero01.weixin.mqdemo.util;import?java.util.Arrays;/** ?*?@program:?mq-demo ?*?@description:?校驗微信發起調用時所傳遞的參數 ?*?@author:?01 ?*?@create:?2018-06-23?17:57 ?**/public?class?WechatMqCheckedUtil?{????//?在公眾平臺上配置的自定義token ????private?static?final?String?token?=?"zeroJun";????/** ?????*?校驗微信加密簽名 ?????* ?????*?@param?signature?微信加密簽名 ?????*?@param?timestamp?時間戳 ?????*?@param?nonce?????隨機字符串 ?????*?@return ?????*/ ????public?static?boolean?checkedSignature(String?signature,?String?timestamp,?String?nonce)?{????????//?1.加入token進行排序 ????????String[]?paramArr?=?new?String[]{token,?timestamp,?nonce}; ????????Arrays.sort(paramArr);????????//?2.拼接成字符串,進行sha1加密 ????????StringBuilder?content?=?new?StringBuilder();????????for?(String?aParamArr?:?paramArr)?{ ????????????content.append(aParamArr); ????????} ????????String?temp?=?SHA1Util.encode(content.toString());????????//?3.與signature參數進行對比,并返回對比結果 ????????return?temp.equals(signature); ????} }
在controller包中,新建一個 WeChatMqController 控制器類,提供給微信調用的接口,代碼如下:
package?org.zero01.weixin.mqdemo.controller;import?org.springframework.web.bind.annotation.GetMapping;import?org.springframework.web.bind.annotation.RequestMapping;import?org.springframework.web.bind.annotation.RequestParam;import?org.springframework.web.bind.annotation.RestController;import?org.zero01.weixin.mqdemo.util.WechatMqCheckedUtil;/** ?*?@program:?mq-demo ?*?@description:?接入微信公眾平臺 ?*?@author:?01 ?*?@create:?2018-06-23?17:51 ?**/@RestController@RequestMapping("/wechat/mq")public?class?WeChatMqController?{????/** ?????*?驗證消息的確來自微信服務器 ?????* ?????*?@param?signature?微信加密簽名 ?????*?@param?timestamp?時間戳 ?????*?@param?nonce?????隨機數 ?????*?@param?echostr???隨機字符串 ?????*?@return ?????*/ ????@GetMapping("/common")????public?String?token(@RequestParam("signature")?String?signature, ????????????????????????@RequestParam("timestamp")?String?timestamp, ????????????????????????@RequestParam("nonce")?String?nonce, ????????????????????????@RequestParam("echostr")?String?echostr)?{????????//?驗證成功則返回echostr ????????if?(WechatMqCheckedUtil.checkedSignature(signature,?timestamp,?nonce))?{ ????????????System.out.println(echostr);????????????return?echostr; ????????}????????return?null; ????} }
完成代碼的編寫,并運行了工程及natapp客戶端后,就可以到公眾平臺上填寫服務器的配置信息了。進入“基本配置” 的頁面,點擊 “修改配置” ,如下:
填寫好基本的配置:
提交配置:
提交成功后,啟用服務器配置:
到此為止,我們的開發者模式就接入完成了。此時,在編輯模式的界面中,可以看到編輯模式下的操作都已失效:
消息的接收與響應
消息管理相關的文檔:
接收普通消息
接收事件推送
我們先來完成文本消息的接收及回復,由于微信傳遞的數據是xml格式的,所以我們需要添加一些用于解析xml的包,在pom.xml中添加如下依賴:
在工程中,新建一個vo包,在該包下新建一個 AllMessage 類,用于封裝所有普通消息的字段。關于不同類型的普通消息所包含的具體字段及描述信息,請參考:接收普通消息。代碼如下:
package?org.zero01.weixin.mqdemo.vo;import?lombok.Data;/** ?*?@program:?mq-demo ?*?@description:?所有類型的消息封裝對象 ?*?@author:?01 ?*?@create:?2018-06-23?21:16 ?**/@Data?//?lombok注解public?class?AllMessage?{????/** ?????*?屬性名首字母大寫的原因是因為返回的xml中標簽的名稱是需要大寫的,否則微信解析不了 ?????*/ ????private?String?ToUserName;??//?接收方賬號 ????private?String?FromUserName;??//?發送方賬號 ????private?long?CreateTime;??//?消息創建時間?(整型) ????private?String?MsgType;??//?消息類型 ????private?String?PicUrl;??//?消息內容 ????private?String?Content;??//?消息內容 ????private?String?MediaId;??//?消息媒體id,可以調用多媒體文件下載接口拉取數據。 ????private?String?Format;??//?語音格式,如amr,speex等 ????private?String?Recognition;??//?語音識別結果,UTF8編碼 ????private?String?MsgId;??//?消息id,64位整型 ????private?String?ThumbMediaId;??//?視頻消息縮略圖的媒體id,可以調用多媒體文件下載接口拉取數據。 ????private?String?Location_X;??//?地理位置維度 ????private?String?Location_Y;??//?地理位置經度 ????private?String?Scale;??//?地圖縮放大小 ????private?String?Label;??//?地理位置信息 ????private?String?Title;??//?消息標題 ????private?String?Description;??//?消息描述 ????private?String?Url;??//?消息鏈接 ????private?String?Event;??//?事件類型 ????private?String?EventKey;?//?事件KEY值 ????private?String?Ticket;??//?二維碼的ticket}
新建一個common包,并在該包中,新建一個 MessageTypeEnum 枚舉類,用于存放普通消息的類型。代碼如下:
package?org.zero01.weixin.mqdemo.common;import?lombok.AllArgsConstructor;import?lombok.Getter;/** ?*?@program:?mq-demo ?*?@description:?普通消息類型 ?*?@author:?01 ?*?@create:?2018-06-24?14:00 ?**/@Getter@AllArgsConstructorpublic?enum?MessageTypeEnum?{ ????MSG_TEXT("text"),??//?文本消息類型 ????MSG_IMAGE("image"),?//?圖片消息類型 ????MSG_VOICE("voice"),?//?語音消息類型 ????MSG_VIDEO("video"),?//?視頻消息類型 ????MSG_SHORTVIDEO("shortvideo"),?//?小視頻消息類型 ????MSG_LOCATION("location"),?//?地理位置消息類型 ????MSG_LINK("link"),?//?鏈接消息類型 ????MSG_EVENT("event"),?//?事件消息類型 ????;????private?String?msgType; }
事件消息類型中包含訂閱/取消訂閱兩種事件類型,所以我們也需要增加一個枚舉來存放這兩種事件類型。代碼如下:
package?org.zero01.weixin.mqdemo.common;import?lombok.AllArgsConstructor;import?lombok.Getter;/** ?*?@program:?mq-demo ?*?@description:?事件推送類型 ?*?@author:?01 ?*?@create:?2018-06-24?14:09 ?**/@Getter@AllArgsConstructorpublic?enum?EventType?{ ????EVENT_SUBSCRIBE("subscribe"),??//?訂閱事件類型 ????EVENT_UNSUBSCRIBE("unsubscribe"),??//?取消訂閱事件類型 ????;????private?String?eventType; }
我們希望有一個專門的地方,來配置我們的自動回復內容,所以再次新建一個枚舉類,用于存放自動回復的內容。代碼如下:
package?org.zero01.weixin.mqdemo.common;import?lombok.AllArgsConstructor;import?lombok.Getter;/** ?*?@program:?mq-demo ?*?@description:?回復的內容 ?*?@author:?01 ?*?@create:?2018-06-24?14:09 ?**/@AllArgsConstructor@Getterpublic?enum?ContentEnum?{ ????CONTENT_SUBSCRIBE("你好,歡迎關注zero菌~"), ????CONTENT_NONSUPPORT("暫不支持文本以外的消息回復!"), ????CONTENT_PREFIX("你發送的消息是:"), ????;????private?String?content; }
在util包下,新建一個 MessageUtil 工具類,用于轉換消息數據類型,代碼如下:
package?org.zero01.weixin.mqdemo.util;import?com.thoughtworks.xstream.XStream;import?org.dom4j.Document;import?org.dom4j.DocumentException;import?org.dom4j.Element;import?org.dom4j.io.SAXReader;import?org.zero01.weixin.mqdemo.vo.AllMessage;import?javax.servlet.http.HttpServletRequest;import?java.io.IOException;import?java.io.InputStream;import?java.util.HashMap;import?java.util.List;import?java.util.Map;/** ?*?@program:?mq-demo ?*?@description:?轉換消息數據類型的工具類 ?*?@author:?01 ?*?@create:?2018-06-23?21:04 ?**/public?class?MessageUtil?{????private?final?static?String?XML?=?"xml";????/** ?????*?xml轉換為map集合 ?????* ?????*?@param?request ?????*?@return ?????*?@throws?IOException ?????*?@throws?DocumentException ?????*/ ????public?static?Map
最后在 WeChatMqController 控制器類中,新增接收微信公眾號消息的接口。注意,接口映射的uri也是/wechat/mq/common,但請求方式是post。代碼如下:
/** ?*?接收微信公眾號消息的接口 ?* ?*?@param?xmlStr ?*?@return ?*/@PostMapping("/common")public?String?text(@RequestBody?String?xmlStr)?{????//?將xml格式的數據,轉換為?AllMessage?對象 ????AllMessage?allMessage?=?MessageUtil.xmlToAllMessage(xmlStr);????//?是否是文本消息類型 ????if?(allMessage.getMsgType().equals(MessageTypeEnum.MSG_TEXT.getMsgType()))?{????????//?自動回復用戶所發送的文本消息 ????????return?MessageUtil.autoReply(allMessage,?ContentEnum.CONTENT_PREFIX.getContent()?+?allMessage.getContent()); ????}????//?是否是事件推送類型 ????else?if?(allMessage.getMsgType().equals(MessageTypeEnum.MSG_EVENT.getMsgType()))?{????????//?是否為訂閱事件,即公眾號被關注時所觸發的事件 ????????if?(EventType.EVENT_SUBSCRIBE.getEventType().equals(allMessage.getEvent()))?{????????????//?自動回復歡迎語 ????????????return?MessageUtil.autoReply(allMessage,?ContentEnum.CONTENT_SUBSCRIBE.getContent()); ????????} ????}?else?{????????//?暫不支持文本以外的消息回復 ????????return?MessageUtil.autoReply(allMessage,?ContentEnum.CONTENT_NONSUPPORT.getContent()); ????}????return?MessageUtil.autoReply(allMessage,?ContentEnum.CONTENT_NONSUPPORT.getContent()); }
編寫完以上代碼后,運行SpringBoot工程以及natapp客戶端,接著向公眾號發送各種類型的普通消息,自動回復結果如下:
如上圖,可以看到,當公眾號被關注時,回復了歡迎語。并成功接收了所有類型的普通消息,進行了相應的自動回復。到此為止,我們就完成了公眾號開發模式的接入。
開發者
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。