一、開發前準備 1)微信公眾平臺賬號 訂閱號:個人版用戶,每天可以群發一條消息 服務號:企業版用戶,每天可以群發四條消息 2)外網映射工具—花生殼,讓自己本機tomcat伺服器能然外網訪問 與微信對接的url要具備以下條件: 在公網上能夠訪問 埠只支持80埠 下麵是用花生殼做映射: 訪問外網地址 ...
一、開發前準備
1)微信公眾平臺賬號
訂閱號:個人版用戶,每天可以群發一條消息
服務號:企業版用戶,每天可以群發四條消息
2)外網映射工具—花生殼,讓自己本機tomcat伺服器能然外網訪問
與微信對接的url要具備以下條件:
- 在公網上能夠訪問
- 埠只支持80埠
下麵是用花生殼做映射:
訪問外網地址,可見就可以訪問自己主機上的網站:
如果需要將自己項目部署到線上伺服器上:
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;
}
}