一、開發前准備
1)微信公眾平台賬號
訂閱號:個人版用戶,每天可以群發一條消息
服務號:企業版用戶,每天可以群發四條消息
2)外網映射工具—花生殼,讓自己本機tomcat服務器能然外網訪問
與微信對接的url要具備以下條件:
下面是用花生殼做映射:
訪問外網地址,可見就可以訪問自己主機上的網站:
如果需要將自己項目部署到在線服務器上:
3)在線虛擬主機或服務器(SAE雲引擎、BAE雲引擎、阿裡雲引擎)
4)TortoiseSVN(SVN客戶端軟件)—將代碼上傳到服務器
開發模式和編輯模式是互斥的:
在eclipse+tomcat上開發
微信服務器的驗證要求(詳見微信平台的開發者文檔):
微信通過通過get請求傳進四個參數
signature 微信加密簽名,signature結合了開發者填寫的token參數和請求中的timestamp參數、nonce參數。
timestamp 時間戳
nonce 隨機數
echostr 隨機字符串
開發者通過檢驗signature對請求進行校驗(下面有校驗方式)。若確認此次GET請求來自微信服務器,請原樣返回echostr參數內容,則接入生效,成為開發者成功,否則接入失敗。加密/校驗流程如下:
1)將token、timestamp、nonce三個參數進行字典序排序
2)將三個參數字符串拼接成一個字符串進行sha1加密
3)開發者獲得加密後的字符串可與signature對比,標識該請求來源於微信
其中 token是自定的。
這其中最重要的是對消息的處理,當普通微信用戶向公眾賬號發消息時,微信服務器將POST消息的XML數據包到開發者填寫的URL上。
首先將xml數據進行格式解析,然後對解析獲取的數據進行處理,然後再將處理後的結果再以xml數據形式返回。
不同消息類型的推送XML數據包結構不同:
關於消息管理,詳見文檔。
注意導包:
dom4j包 用來解析xml
xstream包 用來封裝類
WeixinServlet.java
public class WeixinServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String signature=req.getParameter("signature"); String timestamp=req.getParameter("timestamp"); String nonce=req.getParameter("nonce"); String echostr=req.getParameter("echostr"); PrintWriter out = resp.getWriter(); if(CheckUtil.checkSignature(signature, timestamp, nonce)){ out.print(echostr); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); PrintWriter out = resp.getWriter(); try { Map<String,String> map = MessageUtil.xmlToMap(req); String toUserName = map.get("ToUserName"); String fromUserName = map.get("FromUserName"); String msgType = map.get("MsgType"); String content = map.get("Content"); String message = null; //文本類型 if("text".equals(msgType)) { //按關鍵字進行判斷 if("喜歡你".equals(content)){ message=MessageUtil.initText(toUserName, fromUserName,MessageUtil.firstMenu());; }else if("很喜歡你".equals(content)){ message=MessageUtil.initText(toUserName, fromUserName,MessageUtil.secondMenu()); }else if("?".equals(content)||"?".equals(content)){ message=MessageUtil.initText(toUserName, fromUserName, MessageUtil.menuText()); } System.out.println(message); } //關注事件 else if(MessageUtil.MESSAGE_EVENT.equals(msgType)){ String eventType = map.get("Event"); if(MessageUtil.EVENT_SUB.equals(eventType)){ String mycontent = MessageUtil.menuText(); message = MessageUtil.initText(toUserName, fromUserName, mycontent); } } out.print(message); } catch (Exception e) { e.printStackTrace(); }finally{ out.close(); } } }
校驗:
public class CheckUtil { private static final String token="huaweiclub"; public static boolean checkSignature(String signature,String timestamp,String nonce){ String[] arr=new String[]{token,timestamp,nonce}; //排序 Arrays.sort(arr); //生成字符串 StringBuffer content=new StringBuffer(); for(int i=0;i<arr.length;i++){ content.append(arr[i]); } //sha1加密 String temp=getSha1(content.toString()); return temp.equals(signature); } public static String getSha1(String str){ if(str==null||str.length()==0){ return null; } char hexDigits[] = {'0','1','2','3','4','5','6','7','8','9', 'a','b','c','d','e','f'}; try { MessageDigest mdTemp = MessageDigest.getInstance("SHA1"); mdTemp.update(str.getBytes("UTF-8")); byte[] md = mdTemp.digest(); int j = md.length; char buf[] = new char[j*2]; int k = 0; for (int i = 0; i < j; i++) { byte byte0 = md[i]; buf[k++] = hexDigits[byte0 >>> 4 & 0xf]; buf[k++] = hexDigits[byte0 & 0xf]; } return new String(buf); } catch (Exception e) { // TODO: handle exception return null; } } }
消息的格式轉化:
/* * xml和javabean的轉換 */ public class MessageUtil { /* * 定義不同的消息類型 */ public static final String MESSAGE_TEXT = "text"; public static final String MESSAGE_IMAGE = "image"; public static final String MESSAGE_VOICE = "voice"; public static final String MESSAGE_VIDEO = "video"; public static final String MESSAGE_LINK = "link"; public static final String MESSAGE_LOCATION = "location"; public static final String MESSAGE_EVENT = "event"; public static final String EVENT_SUB = "subscribe"; public static final String EVENT_UNSUB = "unsubscribe"; public static final String EVENT_CLICK = "CLICK"; public static final String EVENT_VIEW = "VIEW"; /** * xml轉為map * @param request * @return * @throws DocumentException * @throws IOException */ public static Map<String, String> xmlToMap(HttpServletRequest request ) throws DocumentException, IOException { Map<String,String> map = new HashMap<String, String>(); //SAXreader用於解析xml SAXReader reader = new SAXReader(); InputStream ins = request.getInputStream(); //從輸入流中讀取xml文檔 Document doc = reader.read(ins); //取得root節點 Element root = doc.getRootElement(); List<Element> list = root.elements(); for (Element e : list) { map.put(e.getName(), e.getText()); } ins.close(); return map; } /* * 將文本消息轉換為xml類型 * xstream能將bean對象序列化xml */ public static String textMessageToXml(TextMessage textMessage){ XStream xstream = new XStream(); //將序列化中的類全量名稱,用別名替換 xstream.alias("xml", textMessage.getClass()); return xstream.toXML(textMessage); } public static String initText(String toUserName, String fromUserName, String content){ TextMessage text = new TextMessage(); text.setFromUserName(toUserName); text.setToUserName(fromUserName); text.setMsgType(MESSAGE_TEXT); text.setCreateTime(new Date().getTime()); text.setContent(content); return textMessageToXml(text); } /* * 根據關鍵字回復內容 */ public static String menuText(){ StringBuffer sb = new StringBuffer(); sb.append("謝謝關注"); return sb.toString(); } public static String firstMenu(){ StringBuffer sb = new StringBuffer(); sb.append("好感+1"); return sb.toString(); } public static String secondMenu(){ StringBuffer sb = new StringBuffer(); sb.append("好感+2"); return sb.toString(); } }
xml文檔的bean對象:
package com.xidian.bean; /** 微信通信用的是XML,所以我們要新建一個javabean */ public class TextMessage { private String ToUserName; private String FromUserName; private long CreateTime; private String MsgType; private String Content; private long MsgId; 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 l) { CreateTime = l; } public String getMsgType() { return MsgType; } public void setMsgType(String msgType) { MsgType = msgType; } public String getContent() { return Content; } public void setContent(String content) { Content = content; } public long getMsgId() { return MsgId; } public void setMsgId(long msgId) { MsgId = msgId; } }