基於以前的文章SSM配置的項目:http://www.cnblogs.com/mangyang/p/5168291.htmljavascript
來進行微信第三方開發,html
微信二次開發,官方仍是網上有不少介紹了,這裏就不在進行講述了 直接上乾貨。java
首先 與微信對接,服務器配置,須要80端口和443端口開放的服務器,這裏推薦 使用 python 的pagekite,一款反向代理的工具,具體安裝百度搜,提供下配置放方法:http://jingyan.baidu.com/article/0eb457e52ca0af03f0a90568.htmlpython
配置好了以後,使用maven 或者 tomcat 等中間件 啓動我們的項目,訪問以前註冊的 pagekite地址,試一下,正常訪問就進行下一步,web
1、服務器配置認證算法
首先根據官方文檔得知,spring
1)將token、timestamp、nonce三個參數進行字典序排序express
2)將三個參數字符串拼接成一個字符串進行sha1加密apache
3)開發者得到加密後的字符串可與signature對比,標識該請求來源於微信json
SHA1.java
package com.wx.util; /* * 微信公衆平臺(JAVA) SDK * * Copyright (c) 2014, Ansitech Network Technology Co.,Ltd All rights reserved. * http://www.ansitech.com/weixin/sdk/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.security.MessageDigest; /** * <p> * Title: SHA1算法 * </p> * */ public final class SHA1 { 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 (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[bytes[j] & 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); } } }
而後編寫controller 來接收 微信的 認證請求
WxManagerController.java
package com.controller; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.wx.util.SHA1; @Controller @RequestMapping( "/wx" ) public class WxManagerController { private String Token = "testfortoken"; @RequestMapping(value = "", method = { RequestMethod.GET, RequestMethod.POST }) @ResponseBody public void load(Model model, HttpServletRequest request, HttpServletResponse response) { //判斷訪問方式 boolean isGet = request.getMethod().toLowerCase().equals("get"); if (isGet) { //進行認證 access(request, response); } else { //處理微信post請求 } } /** * 驗證URL真實性 * * @param request * @param response * @return String */ private String access(HttpServletRequest request, HttpServletResponse response) { // 驗證URL真實性 String signature = request.getParameter("signature");// 微信加密簽名 String timestamp = request.getParameter("timestamp");// 時間戳 String nonce = request.getParameter("nonce");// 隨機數 String echostr = request.getParameter("echostr");// 隨機字符串 List<String> params = new ArrayList<String>(); params.add(Token); params.add(timestamp); params.add(nonce); // 1. 將token、timestamp、nonce三個參數進行字典序排序 Collections.sort(params, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } }); // 2. 將三個參數字符串拼接成一個字符串進行sha1加密 String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2)); if (temp.equals(signature)) { try { response.getWriter().write(echostr); return echostr; } catch (IOException e) { e.printStackTrace(); } } return null; } }
OK,到微信公衆號後臺,開發-基本配置-修改配置
token是項目裏本身填寫的參數,一致便可,
EncodingAESKey 隨機生成便可,
點擊提交。、
成功!
下面作一些功能,用戶消息處理以及事件處理,
2、消息,事件處理
根據官方文檔得知,處理微信的post請求,微信是以xml格式發送的,因此要解析xml並作處理,這裏使用XStream
maven 使用的 oschina庫 搜不到 xstream-1.4.8 版本 手動下載了一個 下載連接:http://pan.baidu.com/s/1dEAdDVb
其餘的maven 可直接pom裏配置。
pom.xml
<dependency> <groupId>xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.8</version> <scope>system</scope> <systemPath>${project.basedir}/WebContent/WEB-INF/lib/xstream-1.4.8.jar</systemPath> </dependency> <dependency> <groupId>xmlpull</groupId> <artifactId>xmlpull</artifactId> <version>1.1.3.1</version> </dependency> <dependency> <groupId>xpp3</groupId> <artifactId>xpp3</artifactId> <version>1.1.4c</version> </dependency>
xstream 下的 systemPath 填寫項目裏對應的jar位置。
開始編寫代碼
加入 CDATA 驗證建立的@interface類
XStreamCDATA.java
package com.penuel.mythopoet.wx.manager; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD }) public @interface XStreamCDATA { }
XStream工具類
SerializeXmlUtil.java
package com.wx.util; import java.io.Writer; import java.lang.reflect.Field; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.annotations.XStreamAlias; import com.thoughtworks.xstream.core.util.QuickWriter; import com.thoughtworks.xstream.io.HierarchicalStreamWriter; import com.thoughtworks.xstream.io.xml.PrettyPrintWriter; import com.thoughtworks.xstream.io.xml.XppDriver; /** * xml 轉換工具類 */ public class SerializeXmlUtil { public static XStream createXstream() { return new XStream(new XppDriver() { @Override public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { boolean cdata = false; Class<?> targetClass = null; @Override public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) { super.startNode(name, clazz); // 業務處理,對於用XStreamCDATA標記的Field,須要加上CDATA標籤 if (!name.equals("xml")) { cdata = needCDATA(targetClass, name); } else { targetClass = clazz; } } @Override protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); } private static boolean needCDATA(Class<?> targetClass, String fieldAlias) { boolean cdata = false; // first, scan self cdata = existsCDATA(targetClass, fieldAlias); if (cdata) return cdata; // if cdata is false, scan supperClass until java.lang.Object Class<?> superClass = targetClass.getSuperclass(); while (!superClass.equals(Object.class)) { cdata = existsCDATA(superClass, fieldAlias); if (cdata) return cdata; superClass = superClass.getClass().getSuperclass(); } return false; } private static boolean existsCDATA(Class<?> clazz, String fieldAlias) { if ("MediaId".equals(fieldAlias)) { return true; // 特例添加 morning99 } // scan fields Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // 1. exists XStreamCDATA if (field.getAnnotation(XStreamCDATA.class) != null) { XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class); // 2. exists XStreamAlias if (null != xStreamAlias) { if (fieldAlias.equals(xStreamAlias.value()))// matched return true; } else {// not exists XStreamAlias if (fieldAlias.equals(field.getName())) return true; } } } return false; } }
微信誤參數字典
WxErrorCode.java
package com.wx.util; import java.util.HashMap; import java.util.Map; @SuppressWarnings("unchecked") public class WxErrorCode { @SuppressWarnings("rawtypes") public static final Map ERRORCODE=new HashMap<Integer,String>(); static{ ERRORCODE.put(-1,"系統繁忙"); ERRORCODE.put(0,"請求成功"); ERRORCODE.put(40001,"獲取access_token時AppSecret錯誤,或者access_token無效"); ERRORCODE.put(40002,"不合法的憑證類型"); ERRORCODE.put(40003,"不合法的OpenID"); ERRORCODE.put(40004,"不合法的媒體文件類型"); ERRORCODE.put(40005,"不合法的文件類型"); ERRORCODE.put(40006,"不合法的文件大小"); ERRORCODE.put(40007,"不合法的媒體文件id"); ERRORCODE.put(40008,"不合法的消息類型"); ERRORCODE.put(40009,"不合法的圖片文件大小"); ERRORCODE.put(40010,"不合法的語音文件大小"); ERRORCODE.put(40011,"不合法的視頻文件大小"); ERRORCODE.put(40012,"不合法的縮略圖文件大小"); ERRORCODE.put(40013,"不合法的APPID"); ERRORCODE.put(40014,"不合法的access_token"); ERRORCODE.put(40015,"不合法的菜單類型"); ERRORCODE.put(40016,"不合法的按鈕個數"); ERRORCODE.put(40017,"不合法的按鈕個數"); ERRORCODE.put(40018,"不合法的按鈕名字長度"); ERRORCODE.put(40019,"不合法的按鈕KEY長度"); ERRORCODE.put(40020,"不合法的按鈕URL長度"); ERRORCODE.put(40021,"不合法的菜單版本號"); ERRORCODE.put(40022,"不合法的子菜單級數"); ERRORCODE.put(40023,"不合法的子菜單按鈕個數"); ERRORCODE.put(40024,"不合法的子菜單按鈕類型"); ERRORCODE.put(40025,"不合法的子菜單按鈕名字長度"); ERRORCODE.put(40026,"不合法的子菜單按鈕KEY長度"); ERRORCODE.put(40027,"不合法的子菜單按鈕URL長度"); ERRORCODE.put(40028,"不合法的自定義菜單使用用戶"); ERRORCODE.put(40029,"不合法的oauth_code"); ERRORCODE.put(40030,"不合法的refresh_token"); ERRORCODE.put(40031,"不合法的openid列表"); ERRORCODE.put(40032,"不合法的openid列表長度"); ERRORCODE.put(40033,"不合法的請求字符,不能包含\\uxxxx格式的字符"); ERRORCODE.put(40035,"不合法的參數"); ERRORCODE.put(40038,"不合法的請求格式"); ERRORCODE.put(40039,"不合法的URL長度"); ERRORCODE.put(40050,"不合法的分組id"); ERRORCODE.put(40051,"分組名字不合法"); ERRORCODE.put(41001,"缺乏access_token參數"); ERRORCODE.put(41002,"缺乏appid參數"); ERRORCODE.put(41003,"缺乏refresh_token參數"); ERRORCODE.put(41004,"缺乏secret參數"); ERRORCODE.put(41005,"缺乏多媒體文件數據"); ERRORCODE.put(41006,"缺乏media_id參數"); ERRORCODE.put(41007,"缺乏子菜單數據"); ERRORCODE.put(41008,"缺乏oauth code"); ERRORCODE.put(41009,"缺乏openid"); ERRORCODE.put(42001,"access_token超時"); ERRORCODE.put(42002,"refresh_token超時"); ERRORCODE.put(42003,"oauth_code超時"); ERRORCODE.put(43001,"須要GET請求"); ERRORCODE.put(43002,"須要POST請求"); ERRORCODE.put(43003,"須要HTTPS請求"); ERRORCODE.put(43004,"須要接收者關注"); ERRORCODE.put(43005,"須要好友關係"); ERRORCODE.put(44001,"多媒體文件爲空"); ERRORCODE.put(44002,"POST的數據包爲空"); ERRORCODE.put(44003,"圖文消息內容爲空"); ERRORCODE.put(44004,"文本消息內容爲空"); ERRORCODE.put(45001,"多媒體文件大小超過限制"); ERRORCODE.put(45002,"消息內容超過限制"); ERRORCODE.put(45003,"標題字段超過限制"); ERRORCODE.put(45004,"描述字段超過限制"); ERRORCODE.put(45005,"連接字段超過限制"); ERRORCODE.put(45006,"圖片連接字段超過限制"); ERRORCODE.put(45007,"語音播放時間超過限制"); ERRORCODE.put(45008,"圖文消息超過限制"); ERRORCODE.put(45009,"接口調用超過限制"); ERRORCODE.put(45010,"建立菜單個數超過限制"); ERRORCODE.put(45015,"回覆時間超過限制"); ERRORCODE.put(45016,"系統分組,不容許修改"); ERRORCODE.put(45017,"分組名字過長"); ERRORCODE.put(45018,"分組數量超過上限"); ERRORCODE.put(46001,"不存在媒體數據"); ERRORCODE.put(46002,"不存在的菜單版本"); ERRORCODE.put(46003,"不存在的菜單數據"); ERRORCODE.put(46004,"不存在的用戶"); ERRORCODE.put(47001,"解析JSON/XML內容錯誤"); ERRORCODE.put(48001,"api功能未受權"); ERRORCODE.put(50001,"用戶未受權該api"); } }
消息工具類
WxMessageUtil.java
package com.wx.util; /** * 消息工具類 * */ public class WxMessageUtil { /** * 返回消息類型:文本 */ public static final String RESP_MESSAGE_TYPE_TEXT = "text"; /** * 返回消息類型:音樂 */ public static final String RESP_MESSAGE_TYPE_MUSIC = "music"; /** * 返回消息類型:圖文 */ public static final String RESP_MESSAGE_TYPE_NEWS = "news"; /** * 請求消息類型:文本 */ public static final String REQ_MESSAGE_TYPE_TEXT = "text"; /** * 請求消息類型:圖片 */ public static final String REQ_MESSAGE_TYPE_IMAGE = "image"; /** * 請求消息類型:連接 */ public static final String REQ_MESSAGE_TYPE_LINK = "link"; /** * 請求消息類型:地理位置 */ public static final String REQ_MESSAGE_TYPE_LOCATION = "location"; /** * 請求消息類型:音頻 */ public static final String REQ_MESSAGE_TYPE_VOICE = "voice"; /** * 請求消息類型:推送 */ public static final String REQ_MESSAGE_TYPE_EVENT = "event"; /** * 事件類型:subscribe(訂閱) */ public static final String EVENT_TYPE_SUBSCRIBE = "subscribe"; /** * 事件類型:unsubscribe(取消訂閱) */ public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe"; /** * 事件類型:CLICK(自定義菜單點擊事件) */ public static final String EVENT_TYPE_CLICK = "CLICK"; }
微信POST的XML數據包轉換爲消息接受對象
InputMessage.java
package com.penuel.mythopoet.wx.manager; /* * 微信公衆平臺(JAVA) SDK * * Copyright (c) 2014, Ansitech Network Technology Co.,Ltd All rights reserved. * http://www.ansitech.com/weixin/sdk/ * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import java.io.Serializable; import com.thoughtworks.xstream.annotations.XStreamAlias; /** * POST的XML數據包轉換爲消息接受對象 * * <p> * 因爲POST的是XML數據包,因此不肯定爲哪一種接受消息,<br/> * 因此直接將全部字段都進行轉換,最後根據<tt>MsgType</tt>字段來判斷取何種數據 * </p> * */ @XStreamAlias("xml") public class InputMessage implements Serializable { /** * */ private static final long serialVersionUID = 1L; @XStreamAlias("ToUserName") private String ToUserName; @XStreamAlias("FromUserName") private String FromUserName; @XStreamAlias("CreateTime") private Long CreateTime; @XStreamAlias("MsgType") private String MsgType = "text"; @XStreamAlias("MsgId") private Long MsgId; // 文本消息 @XStreamAlias("Content") private String Content; // 圖片消息 @XStreamAlias("PicUrl") private String PicUrl; // 位置消息 @XStreamAlias("LocationX") private String LocationX; @XStreamAlias("LocationY") private String LocationY; @XStreamAlias("Scale") private Long Scale; @XStreamAlias("Label") private String Label; // 連接消息 @XStreamAlias("Title") private String Title; @XStreamAlias("Description") private String Description; @XStreamAlias("Url") private String URL; // 語音信息 @XStreamAlias("MediaId") private String MediaId; @XStreamAlias("Format") private String Format; @XStreamAlias("Recognition") private String Recognition; // 事件 @XStreamAlias("Event") private String Event; @XStreamAlias("EventKey") private String EventKey; @XStreamAlias("Ticket") private String Ticket; public String getToUserName() { return ToUserName; } public void setToUserName(String toUserName) { ToUserName = toUserName; } public String getFromUserName() { return FromUserName; } public void setFromUserName(String fromUserName) { FromUserName = fromUserName; } public Long getCreateTime() { return CreateTime; } public void setCreateTime(Long createTime) { CreateTime = createTime; } public String getMsgType() { return MsgType; } public void setMsgType(String msgType) { MsgType = msgType; } public Long getMsgId() { return MsgId; } public void setMsgId(Long msgId) { MsgId = msgId; } public String getContent() { return Content; } public void setContent(String content) { Content = content; } public String getPicUrl() { return PicUrl; } public void setPicUrl(String picUrl) { PicUrl = picUrl; } public String getLocationX() { return LocationX; } public void setLocationX(String locationX) { LocationX = locationX; } public String getLocationY() { return LocationY; } public void setLocationY(String locationY) { LocationY = locationY; } public Long getScale() { return Scale; } public void setScale(Long scale) { Scale = scale; } public String getLabel() { return Label; } public void setLabel(String label) { Label = label; } public String getTitle() { return Title; } public void setTitle(String title) { Title = title; } public String getDescription() { return Description; } public void setDescription(String description) { Description = description; } public String getURL() { return URL; } public void setURL(String uRL) { URL = uRL; } public String getEvent() { return Event; } public void setEvent(String event) { Event = event; } public String getEventKey() { return EventKey; } public void setEventKey(String eventKey) { EventKey = eventKey; } public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } public String getFormat() { return Format; } public void setFormat(String format) { Format = format; } public String getRecognition() { return Recognition; } public void setRecognition(String recognition) { Recognition = recognition; } public String getTicket() { return Ticket; } public void setTicket(String ticket) { Ticket = ticket; } }
xml輸出配置
OutputMessage.java
package com.wx.util; import com.thoughtworks.xstream.annotations.XStreamAlias; @XStreamAlias("xml") public class OutputMessage { @XStreamAlias("ToUserName") @XStreamCDATA private String ToUserName; @XStreamAlias("FromUserName") @XStreamCDATA private String FromUserName; @XStreamAlias("CreateTime") private Long CreateTime; @XStreamAlias("MsgType") @XStreamCDATA private String MsgType = "text"; private ImageMessage Image; public String getToUserName() { return ToUserName; } public void setToUserName(String toUserName) { ToUserName = toUserName; } public String getFromUserName() { return FromUserName; } public void setFromUserName(String fromUserName) { FromUserName = fromUserName; } public Long getCreateTime() { return CreateTime; } public void setCreateTime(Long createTime) { CreateTime = createTime; } public String getMsgType() { return MsgType; } public void setMsgType(String msgType) { MsgType = msgType; } public ImageMessage getImage() { return Image; } public void setImage(ImageMessage image) { Image = image; } }
圖片消息支持
MediaIdMessage.java
package com.wx.util; import com.thoughtworks.xstream.annotations.XStreamAlias; public class MediaIdMessage { @XStreamAlias("MediaId") @XStreamCDATA private String MediaId; public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } }
ImageMessage.java
package com.wx.util; import com.thoughtworks.xstream.annotations.XStreamAlias; @XStreamAlias("Image") public class ImageMessage extends MediaIdMessage { }
ok 以上配置完成後便可 編寫post處理controller
WxManagerController.java
修改後最終
package com.controller; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.thoughtworks.xstream.XStream; import com.wx.util.ImageMessage; import com.wx.util.InputMessage; import com.wx.util.OutputMessage; import com.wx.util.SHA1; import com.wx.util.SerializeXmlUtil; import com.wx.util.WxMessageUtil; @Controller @RequestMapping( "/wx" ) public class WxManagerController { private String Token = "testfortoken"; @RequestMapping(value = "", method = { RequestMethod.GET, RequestMethod.POST }) @ResponseBody public void load(Model model, HttpServletRequest request, HttpServletResponse response) { //判斷訪問方式 boolean isGet = request.getMethod().toLowerCase().equals("get"); if (isGet) { //進行認證 access(request, response); } else { try { //轉碼UTF-8,防止亂碼 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding( "utf-8" ); //處理微信post請求 acceptMessage(request,response); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 驗證URL真實性 * * @param request * @param response * @return String */ private String access(HttpServletRequest request, HttpServletResponse response) { // 驗證URL真實性 String signature = request.getParameter("signature");// 微信加密簽名 String timestamp = request.getParameter("timestamp");// 時間戳 String nonce = request.getParameter("nonce");// 隨機數 String echostr = request.getParameter("echostr");// 隨機字符串 List<String> params = new ArrayList<String>(); params.add(Token); params.add(timestamp); params.add(nonce); // 1. 將token、timestamp、nonce三個參數進行字典序排序 Collections.sort(params, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } }); // 2. 將三個參數字符串拼接成一個字符串進行sha1加密 String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2)); if (temp.equals(signature)) { try { response.getWriter().write(echostr); return echostr; } catch (IOException e) { e.printStackTrace(); } } return null; } private void acceptMessage(HttpServletRequest request, HttpServletResponse response) throws IOException { // 處理接收消息 ServletInputStream in = request.getInputStream(); // 將POST流轉換爲XStream對象 XStream xs = SerializeXmlUtil.createXstream(); xs.processAnnotations(InputMessage.class); xs.processAnnotations(OutputMessage.class); // 將指定節點下的xml節點數據映射爲對象 xs.alias("xml", InputMessage.class); // 將流轉換爲字符串 StringBuilder xmlMsg = new StringBuilder(); byte[] b = new byte[4096]; for (int n; (n = in.read(b)) != -1;) { xmlMsg.append(new String(b, 0, n, "UTF-8")); } // 將xml內容轉換爲InputMessage對象 InputMessage inputMsg = (InputMessage) xs.fromXML(xmlMsg.toString()); String servername = inputMsg.getToUserName();// 服務端 String custermname = inputMsg.getFromUserName();// 客戶端 long createTime = inputMsg.getCreateTime();// 接收時間 Long returnTime = Calendar.getInstance().getTimeInMillis() / 1000;// 返回時間 // 取得消息類型 String msgType = inputMsg.getMsgType(); // 根據消息類型獲取對應的消息內容 if (msgType.equals(WxMessageUtil.REQ_MESSAGE_TYPE_TEXT)) { // 文本消息 StringBuffer str = new StringBuffer(); str.append("<xml>"); str.append("<ToUserName><![CDATA[" + custermname + "]]></ToUserName>"); str.append("<FromUserName><![CDATA[" + servername + "]]></FromUserName>"); str.append("<CreateTime>" + returnTime + "</CreateTime>"); str.append("<MsgType><![CDATA[" + msgType + "]]></MsgType>"); str.append("<Content><![CDATA[您發送的是:" + inputMsg.getContent() + "?]]></Content>"); str.append("</xml>"); response.getWriter().write(str.toString()); } // 獲取並返回多圖片消息 else if(msgType.equals(WxMessageUtil.REQ_MESSAGE_TYPE_IMAGE)) { System.out.println("獲取多媒體信息"); String mediaId = inputMsg.getMediaId();//多媒體文件id String picUrl = inputMsg.getPicUrl();//圖片連接 long msgId = inputMsg.getMsgId();//消息id,64位整型 OutputMessage outputMsg = new OutputMessage(); outputMsg.setFromUserName(servername); outputMsg.setToUserName(custermname); outputMsg.setCreateTime(returnTime); outputMsg.setMsgType(msgType); ImageMessage images = new ImageMessage(); images.setMediaId(mediaId); outputMsg.setImage(images); response.getWriter().write(xs.toXML(outputMsg)); } //事件 else if (msgType.equals(WxMessageUtil.REQ_MESSAGE_TYPE_EVENT)) { // 事件類型 String eventType = inputMsg.getEvent(); if (eventType.equals(WxMessageUtil.EVENT_TYPE_SUBSCRIBE)) { // 關注 }else if (eventType.equals(WxMessageUtil.EVENT_TYPE_UNSUBSCRIBE)) { //取消關注 }else if(eventType.equals(WxMessageUtil.EVENT_TYPE_CLICK)){ //點擊 } } } }
啓動測試,從新提交一下服務器認證。
效果:
完成,進行下一個功能 素材庫管理
3、素材庫
操做素材庫須要 access_token 接口調用憑據,
下面編寫 access_token 方法。
建立 AccessToken model
AccessToken.java
package com.wx.model; public class AccessToken { // 獲取到的憑證 private String token; // 憑證有效時間,單位:秒 private int expiresIn; public String getToken() { return token; } public void setToken(String token) { this.token = token; } public int getExpiresIn() { return expiresIn; } public void setExpiresIn(int expiresIn) { this.expiresIn = expiresIn; } }
想微信發起post請求必須使用https方式,因此須要編寫配置https請求方式,證書配置以下,
MyX509TrustManager.java
package com.wx.util; import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; public class MyX509TrustManager implements X509TrustManager { public void checkClientTrusted(X509Certificate[] chain, String authType) { } public void checkServerTrusted(X509Certificate[] chain, String authType) { } public X509Certificate[] getAcceptedIssuers() { return null; } }
配置後編寫處理https發起以及處理,和獲取accesstoken方法
WxManagerUtil.java
package com.wx.util; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.net.ConnectException; import java.net.URL; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.TrustManager; import com.alibaba.fastjson.JSONException; import com.alibaba.fastjson.JSONObject;
import com.wx.model.AccessToken; public class WxManagerUtil { // 獲取access_token的接口地址(GET) 限200(次/天) public final static String access_token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET"; /** * 發起https請求並獲取結果 * * @param requestUrl 請求地址 * @param requestMethod 請求方式(GET、POST) * @param outputStr 提交的數據 * @return JSONObject(經過JSONObject.get(key)的方式獲取json對象的屬性值) */ public static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) { JSONObject jsonObject = null; StringBuffer buffer = new StringBuffer(); try { // 建立SSLContext對象,並使用咱們指定的信任管理器初始化 TrustManager[] tm = { new MyX509TrustManager() }; SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE"); sslContext.init(null, tm, new java.security.SecureRandom()); // 從上述SSLContext對象中獲得SSLSocketFactory對象 SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl); HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection(); httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true); httpUrlConn.setDoInput(true); httpUrlConn.setUseCaches(false); // 設置請求方式(GET/POST) httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod)) httpUrlConn.connect(); // 當有數據須要提交時 if (null != outputStr) { OutputStream outputStream = httpUrlConn.getOutputStream(); // 注意編碼格式,防止中文亂碼 outputStream.write(outputStr.getBytes("UTF-8")); outputStream.close(); } // 將返回的輸入流轉換成字符串 InputStream inputStream = httpUrlConn.getInputStream(); InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8"); BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null; while ((str = bufferedReader.readLine()) != null) { buffer.append(str); } bufferedReader.close(); inputStreamReader.close(); // 釋放資源 inputStream.close(); inputStream = null; httpUrlConn.disconnect(); jsonObject = JSONObject.parseObject(buffer.toString()); } catch (ConnectException ce) { System.out.println("微信服務器鏈接超時!"); } catch (Exception e) { System.out.println("HTTPS請求錯誤,錯誤信息:\n" + e.getMessage()); } return jsonObject; } /** * 生成AccessToken * @param appid * @param appsecret * @return */ public static AccessToken getAccessToken(String appid, String appsecret) { AccessToken accessToken = null; String requestUrl = access_token_url.replace("APPID", appid).replace( "APPSECRET", appsecret); JSONObject jsonObject = httpRequest(requestUrl, "GET", null); // 若是請求成功 if (null != jsonObject) { try { accessToken = new AccessToken(); accessToken.setToken(jsonObject.getString("access_token")); accessToken.setExpiresIn(jsonObject.getIntValue("expires_in")); } catch (JSONException e) { accessToken = null; // 獲取token失敗 System.out.println("獲取TOKEN失敗("+jsonObject.getString("errcode")+")"); } } return accessToken; } }
建立素材庫model
WxArticles.java
package com.wx.model; public class WxArticles { private String title; //標題 private String thumb_media_id;//圖文消息的封面圖片素材id private String author;//做者 private String digest;//圖文消息的摘要,僅有單圖文消息纔有摘要,多圖文此處爲空 private String show_cover_pic;//是否顯示封面,0爲false,即不顯示,1爲true,即顯示 private String content;// 圖文消息的具體內容,支持HTML標籤,必須少於2萬字符,小於1M,且此處會去除JS private String content_source_url;//圖文消息的原文地址,即點擊「閱讀原文」後的URL public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getThumb_media_id() { return thumb_media_id; } public void setThumb_media_id(String thumb_media_id) { this.thumb_media_id = thumb_media_id; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } public String getDigest() { return digest; } public void setDigest(String digest) { this.digest = digest; } public String getShow_cover_pic() { return show_cover_pic; } public void setShow_cover_pic(String show_cover_pic) { this.show_cover_pic = show_cover_pic; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getContent_source_url() { return content_source_url; } public void setContent_source_url(String content_source_url) { this.content_source_url = content_source_url; } }
完成以上基本OK。
先寫一個測試方法,提交圖文素材 須要thumb_media_id 這個參數,即:圖文消息的封面圖片素材id(必須是永久mediaID)
咱們如今公衆號裏上傳一個圖片素材,而後去獲取他的thumb_media_id後再 提交新的圖文素材,這裏使用用戶發送消息來觸發測試
最終代碼:
WxManagerController.java
package com.controller; import java.io.IOException; import java.util.ArrayList; import java.util.Calendar; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.ServletInputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jdt.internal.compiler.batch.Main; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; import com.alibaba.fastjson.JSONObject; import com.thoughtworks.xstream.XStream; import com.wx.model.AccessToken; import com.wx.model.WxArticles; import com.wx.util.ImageMessage; import com.wx.util.InputMessage; import com.wx.util.OutputMessage; import com.wx.util.SHA1; import com.wx.util.SerializeXmlUtil; import com.wx.util.WxManagerUtil; import com.wx.util.WxMessageUtil; @Controller @RequestMapping( "/wx" ) public class WxManagerController { private String Token = "CL0WQY79GJ12XV643BEZKMF5PHTAN"; @RequestMapping(value = "", method = { RequestMethod.GET, RequestMethod.POST }) @ResponseBody public void load(Model model, HttpServletRequest request, HttpServletResponse response) { //判斷訪問方式 boolean isGet = request.getMethod().toLowerCase().equals("get"); if (isGet) { //進行認證 access(request, response); } else { try { //轉碼UTF-8,防止亂碼 request.setCharacterEncoding("utf-8"); response.setCharacterEncoding( "utf-8" ); //處理微信post請求 acceptMessage(request,response); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /** * 驗證URL真實性 * * @param request * @param response * @return String */ private String access(HttpServletRequest request, HttpServletResponse response) { // 驗證URL真實性 String signature = request.getParameter("signature");// 微信加密簽名 String timestamp = request.getParameter("timestamp");// 時間戳 String nonce = request.getParameter("nonce");// 隨機數 String echostr = request.getParameter("echostr");// 隨機字符串 List<String> params = new ArrayList<String>(); params.add(Token); params.add(timestamp); params.add(nonce); // 1. 將token、timestamp、nonce三個參數進行字典序排序 Collections.sort(params, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } }); // 2. 將三個參數字符串拼接成一個字符串進行sha1加密 String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2)); if (temp.equals(signature)) { try { response.getWriter().write(echostr); return echostr; } catch (IOException e) { e.printStackTrace(); } } return null; } private void acceptMessage(HttpServletRequest request, HttpServletResponse response) throws IOException { // 處理接收消息 ServletInputStream in = request.getInputStream(); // 將POST流轉換爲XStream對象 XStream xs = SerializeXmlUtil.createXstream(); xs.processAnnotations(InputMessage.class); xs.processAnnotations(OutputMessage.class); // 將指定節點下的xml節點數據映射爲對象 xs.alias("xml", InputMessage.class); // 將流轉換爲字符串 StringBuilder xmlMsg = new StringBuilder(); byte[] b = new byte[4096]; for (int n; (n = in.read(b)) != -1;) { xmlMsg.append(new String(b, 0, n, "UTF-8")); } // 將xml內容轉換爲InputMessage對象 InputMessage inputMsg = (InputMessage) xs.fromXML(xmlMsg.toString()); String servername = inputMsg.getToUserName();// 服務端 String custermname = inputMsg.getFromUserName();// 客戶端 long createTime = inputMsg.getCreateTime();// 接收時間 Long returnTime = Calendar.getInstance().getTimeInMillis() / 1000;// 返回時間 // 取得消息類型 String msgType = inputMsg.getMsgType(); // 根據消息類型獲取對應的消息內容 if (msgType.equals(WxMessageUtil.REQ_MESSAGE_TYPE_TEXT)) { // 文本消息 // StringBuffer str = new StringBuffer(); // str.append("<xml>"); // str.append("<ToUserName><![CDATA[" + custermname + "]]></ToUserName>"); // str.append("<FromUserName><![CDATA[" + servername + "]]></FromUserName>"); // str.append("<CreateTime>" + returnTime + "</CreateTime>"); // str.append("<MsgType><![CDATA[" + msgType + "]]></MsgType>"); // str.append("<Content><![CDATA[您發送的是:" + inputMsg.getContent() + "?]]></Content>"); // str.append("</xml>"); // response.getWriter().write(str.toString()); AccessToken at = WxManagerUtil.getAccessToken("你的AppID", "你的AppSecret"); String mediaId = getImgsFor(at.getToken()); addImgTest(at.getToken(),mediaId); } // 獲取並返回多圖片消息 else if(msgType.equals(WxMessageUtil.REQ_MESSAGE_TYPE_IMAGE)) { System.out.println("獲取多媒體信息"); String mediaId = inputMsg.getMediaId();//多媒體文件id String picUrl = inputMsg.getPicUrl();//圖片連接 long msgId = inputMsg.getMsgId();//消息id,64位整型 OutputMessage outputMsg = new OutputMessage(); outputMsg.setFromUserName(servername); outputMsg.setToUserName(custermname); outputMsg.setCreateTime(returnTime); outputMsg.setMsgType(msgType); ImageMessage images = new ImageMessage(); images.setMediaId(mediaId); outputMsg.setImage(images); response.getWriter().write(xs.toXML(outputMsg)); } //事件 else if (msgType.equals(WxMessageUtil.REQ_MESSAGE_TYPE_EVENT)) { // 事件類型 String eventType = inputMsg.getEvent(); if (eventType.equals(WxMessageUtil.EVENT_TYPE_SUBSCRIBE)) { // 關注 }else if (eventType.equals(WxMessageUtil.EVENT_TYPE_UNSUBSCRIBE)) { //取消關注 }else if(eventType.equals(WxMessageUtil.EVENT_TYPE_CLICK)){ //點擊 } } } public String getImgsFor(String token){ //取第一個圖片素材 String geturls = "https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token="+token; String jsonval="{\"type\":\"image\",\"offset\":\"0\",\"count\":\"1\"}"; JSONObject jsonObject = WxManagerUtil.httpRequest(geturls, "POST", jsonval); String result=null; if (null != jsonObject) { JSONObject josns =(JSONObject) jsonObject.getJSONArray("item").get(0); result = josns.getString("media_id"); } return result; } public void addImgTest(String token,String mediaId){ List<WxArticles> list = new ArrayList<WxArticles>(); WxArticles wxArticles = new WxArticles(); wxArticles.setTitle("a title"); wxArticles.setAuthor("a author"); wxArticles.setContent("a content"); wxArticles.setContent_source_url("a content_source_url"); wxArticles.setDigest("a digest"); wxArticles.setShow_cover_pic("a show_cover_pic"); wxArticles.setThumb_media_id(mediaId); list.add(wxArticles); Map<String,List<WxArticles>> maplist = new HashMap<String,List<WxArticles>>(); maplist.put("articles", list); String urls= "https://api.weixin.qq.com/cgi-bin/material/add_news?access_token="+token; String jsons = JSONObject.toJSONString(maplist); JSONObject jsonObject = WxManagerUtil.httpRequest(urls, "POST", jsons); String result=null; if (null != jsonObject) { result = jsonObject.getString("media_id"); System.out.println("返回("+result+")"); } } }
上傳永久素材成功!
別忘了 填寫本身的 appID 和appSecret;
AccessToken at = WxManagerUtil.getAccessToken("你的AppID", "你的AppSecret");
補充: 自定義菜單 功能;
WxManagerController.java 下添加此方法
private void accessMenu(String token){ String menu = "{\"button\":[{\"type\":\"click\",\"name\":\"項目管理\",\"key\":\"20_PROMANAGE\"},{\"type\":\"click\",\"name\":\"機構運做\",\"key\":\"30_ORGANIZATION\"},{\"name\":\"平常工做\",\"sub_button\":[{\"type\":\"click\",\"name\":\"待辦工單\",\"key\":\"01_WAITING\"},{\"type\":\"click\",\"name\":\"已辦工單\",\"key\":\"02_FINISH\"},{\"type\":\"click\",\"name\":\"個人工單\",\"key\":\"03_MYJOB\"},{\"type\":\"click\",\"name\":\"公告消息箱\",\"key\":\"04_MESSAGEBOX\"},{\"type\":\"click\",\"name\":\"簽到\",\"key\":\"05_SIGN\"}]}]}"; String requestUrl = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+token; int result = 0 ; JSONObject jsonObject = WxManagerUtil.httpRequest(requestUrl, "POST", menu); if (null != jsonObject) { if (0 != jsonObject.getIntValue("errcode")) { result = jsonObject.getIntValue("errcode"); System.out.println("建立菜單失敗("+result+")"); } System.out.println("建立成功("+result+")"); } }
完成!