微信公眾號開發者模式介紹及接入

      網友投稿 1291 2022-05-30

      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?xmlToMap(HttpServletRequest?request)?throws?IOException,?DocumentException?{ ????????Map?map?=?new?HashMap<>(); ????????SAXReader?reader?=?new?SAXReader(); ????????InputStream?inputStream?=?request.getInputStream(); ????????Document?document?=?reader.read(inputStream); ????????Element?root?=?document.getRootElement(); ????????List?elementList?=?root.elements();????????for?(Element?element?:?elementList)?{ ????????????map.put(element.getName(),?element.getText()); ????????} ????????inputStream.close();????????return?map; ????}????/** ?????*?將?AllMessage?消息對象,轉換為xml ?????* ?????*?@param?allMessage ?????*?@return ?????*/ ????public?static?String?allMessageToXml(AllMessage?allMessage)?{ ????????XStream?xStream?=?new?XStream(); ????????xStream.alias(XML,?allMessage.getClass());????????return?xStream.toXML(allMessage); ????}????/** ?????*?將?AllMessage?消息對象,轉換為xml,并指定content的內容 ?????* ?????*?@param?allMessage ?????*?@return ?????*/ ????public?static?String?allMessageToXml(AllMessage?allMessage,?String?content)?{ ????????allMessage.setContent(content);????????return?allMessageToXml(allMessage); ????}????/** ?????*?將xml轉換為?AllMessage消息對象 ?????* ?????*?@param?xmlStr ?????*?@return ?????*/ ????public?static?AllMessage?xmlToAllMessage(String?xmlStr)?{ ????????XStream?xStream?=?new?XStream(); ????????AllMessage?allMessage?=?new?AllMessage(); ????????xStream.aliasType(XML,?allMessage.getClass()); ????????allMessage?=?(AllMessage)?xStream.fromXML(xmlStr);????????return?allMessage; ????}????/** ?????*?將xml轉換為?AllMessage?消息對象,并指定content的內容 ?????* ?????*?@param?xmlStr ?????*?@param?content ?????*?@return ?????*/ ????public?static?AllMessage?xmlToAllMessage(String?xmlStr,?String?content)?{ ????????AllMessage?allMessage?=?xmlToAllMessage(xmlStr); ????????allMessage.setContent(content);????????return?allMessage; ????}????/** ?????*?設置并獲取文本消息類型的?AllMessage?對象 ?????*?@param?fromUserName ?????*?@param?toUserName ?????*?@param?content ?????*?@return ?????*/ ????public?static?AllMessage?setGetTextMsg(String?fromUserName,?String?toUserName,?String?content)?{ ????????AllMessage?allMessage?=?new?AllMessage(); ????????allMessage.setFromUserName(toUserName); ????????allMessage.setToUserName(fromUserName); ????????allMessage.setMsgType(MessageTypeEnum.MSG_TEXT.getMsgType()); ????????allMessage.setCreateTime(new?Date().getTime()); ????????allMessage.setContent(content);????????return?allMessage; ????}????/** ?????*?自動回復 ?????*?@param?allMessage ?????*?@param?content ?????*?@return ?????*/ ????public?static?String?autoReply(AllMessage?allMessage,String?content)?{ ????????allMessage?=?setGetTextMsg(allMessage.getFromUserName(),?allMessage.getToUserName(),?content);????????return?allMessageToXml(allMessage); ????} }

      最后在 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小時內刪除侵權內容。

      上一篇:2021年50個酷炫的Web和移動項目創意
      下一篇:介紹一些 ERP 系統軟件開發的常用 IDE
      相關文章
      亚洲一区精品伊人久久伊人| 亚洲精品第一国产综合境外资源| 亚洲中文字幕无码一区| 相泽南亚洲一区二区在线播放| 亚洲无人区视频大全| 亚洲毛片基地日韩毛片基地| 久久久久亚洲AV无码网站| 亚洲国产精品不卡在线电影| 久久久久久亚洲av成人无码国产| 亚洲av中文无码乱人伦在线播放 | 亚洲AV无码国产精品色午友在线| 久久久久国产亚洲AV麻豆| 久久久久国产亚洲AV麻豆| 狠狠综合久久综合88亚洲| 曰韩亚洲av人人夜夜澡人人爽| 日韩精品亚洲aⅴ在线影院| 亚洲午夜久久久影院伊人| 亚洲色大成网站www永久一区| 亚洲精品无码不卡在线播放HE| 亚洲精品无码Av人在线观看国产 | 亚洲成AV人片在线观看ww| 国产A在亚洲线播放| 久久精品国产精品亚洲蜜月| 亚洲AV无码成人网站久久精品大 | 亚洲片国产一区一级在线观看 | 亚洲乱码中文论理电影| 亚洲久悠悠色悠在线播放| 亚洲色大成网站www永久男同| 亚洲妇女无套内射精| 亚洲 自拍 另类小说综合图区| 亚洲乱码日产精品a级毛片久久| 狠狠综合久久综合88亚洲| 亚洲avav天堂av在线不卡| 亚洲综合在线成人一区| 亚洲人成在线精品| 亚洲乱色伦图片区小说| 在线a亚洲v天堂网2018| 亚洲中文字幕日产乱码高清app| 亚洲av无码乱码国产精品| 亚洲综合色丁香麻豆| 亚洲综合久久精品无码色欲|