0、前提条件
1、需要一个公众平台帐号(测试帐号也可以)
2、平台需要被人关注
1、导入jar
commons-beanutils-1.8.0.jar
commons-collections-3.1.jar
commons-lang-2.5.jar
commons-logging-1.2.jar
date4j.jar
dom4j-1.6.1.jar
dom4j-full.jar
ezmorph-1.0.6.jar
json-lib-2.3-jdk15.jar
jstl.jar
spring-aop-4.1.0.RELEASE.jar
spring-aspects-4.1.0.RELEASE.jar
spring-beans-4.1.0.RELEASE.jar
spring-context-4.1.0.RELEASE.jar
spring-context-support-4.1.0.RELEASE.jar
spring-core-4.1.0.RELEASE.jar
spring-expression-4.1.0.RELEASE.jar
spring-jdbc-4.1.0.RELEASE.jar
spring-tx-4.1.0.RELEASE.jar
spring-web-4.1.0.RELEASE.jar
spring-webmvc-4.1.0.RELEASE.jar
standard.jar
xmlbeans-2.3.0.jar
xmlpull-1.1.3.1.jar
xstream-1.4.7.jar
2、配置springMVC环境
这里不贴代码,具体代码请参考“java微信开发-springMVC配置”。
3、接收消息
处理请求
@Controller
public class HelloWorldController {
/**
* 微信消息接收和token验证
* @param model
* @param request
* @param response
* @throws IOException
*/
@RequestMapping("/hello")
public void hello(Model model, HttpServletRequest request,HttpServletResponse response) throws IOException {
boolean isGet = request.getMethod().toLowerCase().equals("get");
PrintWriter print;
if (isGet) {
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (signature != null && CheckoutUtil.checkSignature(signature, timestamp, nonce)) {
try {
print = response.getWriter();
print.write(echostr);
print.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
} else {
// 进入post聊天处理
System.out.println("enter post");
print = response.getWriter();
// 接收消息并返回消息
// 调用核心服务类接收处理请求
String respXml = CoreService.processRequest(request);
print.print(respXml);
print.flush();
print.close();
}
}
}
核心服务类
package com.SpringMVC.web.controller;
import java.util.Date;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import OAuth2.OAuth;
/**
* 核心服务类
*
* @author 32950745
*
*/
public class CoreService {
public static String processRequest(HttpServletRequest request) {
// xml格式的消息数据
String respXml = null;
// 默认返回的文本消息内容
String respContent = "未知的消息类型";
try {
// 调用parseXml方法解析请求消息
Map requestMap = MessageUtil.parseXml(request);
// 发送方帐号
String fromUserName = (String) requestMap.get("FromUserName");
// 开发者微信号
String toUserName = (String) requestMap.get("ToUserName");
// 消息类型
String msgType = (String) requestMap.get("MsgType");
// 回复文本消息
TextMessage textMessage = new TextMessage();
textMessage.setToUserName(fromUserName);
textMessage.setFromUserName(toUserName);
textMessage.setCreateTime(new Date().getTime());
textMessage.setMsgType(MessageUtil.RESP_MESSAGE_TYPE_TEXT);
// 文本消息
if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) {
respContent = "您发送的是文本消息!";
}
// 图片消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_IMAGE)) {
respContent = "您发送的是图片消息!";
}
// 语音消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VOICE)) {
respContent = "您发送的是语音消息!";
}
// 视频消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_VIDEO)) {
respContent = "您发送的是视频消息!";
}
// 地理位置消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LOCATION)) {
respContent = "您发送的是地理位置消息!";
}
// 链接消息
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_LINK)) {
respContent = "您发送的是链接消息!";
}
// 事件推送
else if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_EVENT)) {
// 事件类型
String eventType = (String) requestMap.get("Event");
// 关注
if (eventType.equals(MessageUtil.EVENT_TYPE_SUBSCRIBE)) {
respContent = "谢谢您的关注!";
}
// 取消关注
else if (eventType.equals(MessageUtil.EVENT_TYPE_UNSUBSCRIBE)) {
// TODO 取消订阅后用户不会再收到公众账号发送的消息,因此不需要回复
}
// 扫描带参数二维码
else if (eventType.equals(MessageUtil.EVENT_TYPE_SCAN)) {
// TODO 处理扫描带参数二维码事件
}
// 上报地理位置
else if (eventType.equals(MessageUtil.EVENT_TYPE_LOCATION)) {
// TODO 处理上报地理位置事件
}
// 自定义菜单
else if (eventType.equals(MessageUtil.EVENT_TYPE_CLICK)) {
// TODO 处理菜单点击事件
}
}
// 设置文本消息的内容
textMessage.setContent(respContent);
// 将文本消息对象转换成xml
respXml = MessageUtil.textMessageToXml(textMessage);
return respXml;
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}
消息工具类
package com.SpringMVC.web.controller;
import java.io.InputStream;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.thoughtworks.xstream.XStream;
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;
public class MessageUtil {
//APPID
public static final String appID = "微信公众平台的appid";
//appsecret
public static final String appsecret = "微信公众平台的apsecred";
public static final String RESP_MESSAGE_TYPE_TEXT = "text";
public static final Object REQ_MESSAGE_TYPE_TEXT = "text";
public static final Object REQ_MESSAGE_TYPE_IMAGE = "image";
public static final Object REQ_MESSAGE_TYPE_VOICE = "voice";
public static final Object REQ_MESSAGE_TYPE_VIDEO = "video";
public static final Object REQ_MESSAGE_TYPE_LOCATION = "location";
public static final Object REQ_MESSAGE_TYPE_LINK = "link";
public static final Object REQ_MESSAGE_TYPE_EVENT = "event";
public static final Object EVENT_TYPE_SUBSCRIBE = "SUBSCRIBE";
public static final Object EVENT_TYPE_UNSUBSCRIBE = "UNSUBSCRIBE";
public static final Object EVENT_TYPE_SCAN = "SCAN";
public static final Object EVENT_TYPE_LOCATION = "LOCATION";
public static final Object EVENT_TYPE_CLICK = "CLICK";
public static String PREFIX_CDATA = "<![CDATA[";
public static String SUFFIX_CDATA = "]]>";
/**
* 扩展xstream,使其支持CDATA块
* 由于xstream框架本身并不支持CDATA块的生成,下面代码是对xtream做了扩展,
* 使其支持在生成xml各元素值时添加CDATA块。
* @date 2013-05-19
*/
private static XStream xstream = new XStream(new XppDriver() {
public HierarchicalStreamWriter createWriter(Writer out) {
return new PrettyPrintWriter(out) {
// 对所有xml节点的转换都增加CDATA标记
boolean cdata = true;
@SuppressWarnings("unchecked")
public void startNode(String name, Class clazz) {
super.startNode(name, clazz);
}
protected void writeText(QuickWriter writer, String text) {
if (cdata) {
writer.write(PREFIX_CDATA);
writer.write(text);
writer.write(SUFFIX_CDATA);
} else {
super.writeText(writer, text);
}
}
};
}
});
/**
* 解析微信发来的请求(xml)
*
* @param request
* @return
* @throws Exception
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static Map parseXml(HttpServletRequest request) throws Exception {
// 将解析结果存储在HashMap中
Map map = new HashMap();
// 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList)
map.put(e.getName(), e.getText());
// 释放资源
inputStream.close();
inputStream = null;
return map;
}
/**
* 文本消息对象转换成xml
*
* @param textMessage
* 文本消息对象
* @return xml
*/
public static String textMessageToXml(TextMessage textMessage) {
xstream.alias("xml", textMessage.getClass());
//xstream.processAnnotations(textMessage.getClass());
System.out.println(xstream.toXML(textMessage));
return xstream.toXML(textMessage);
}
/**
* 音乐消息对象转换成xml
*
* @param musicMessage
* 音乐消息对象
* @return xml
*/
public static String musicMessageToXml(MusicMessage musicMessage) {
xstream.alias("xml", musicMessage.getClass());
return xstream.toXML(musicMessage);
}
/**
* 图文消息对象转换成xml
*
* @param newsMessage
* 图文消息对象
* @return xml
*/
public static String newsMessageToXml(NewsMessage newsMessage) {
xstream.alias("xml", newsMessage.getClass());
xstream.alias("item", new Article().getClass());
return xstream.toXML(newsMessage);
}
}
所有消息基类
package com.SpringMVC.web.controller;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 消息基类(公众帐号 -> 普通用户)
* @author 32950745
*
*/
public class BaseMessage {
// 接收方帐号(收到的OpenID)
@XStreamAlias("ToUserName")
private String ToUserName;
// 开发者微信号
@XStreamAlias("FromUserName")
private String FromUserName;
// 消息创建时间 (整型)
@XStreamAlias("CreateTime")
private long CreateTime;
// 消息类型(text/music/news)
@XStreamAlias("MsgType")
private String MsgType;
// 位0x0001被标志时,星标刚收到的消息
@XStreamAlias("FuncFlag")
private int FuncFlag;
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 int getFuncFlag() {
return FuncFlag;
}
public void setFuncFlag(int funcFlag) {
FuncFlag = funcFlag;
}
@Override
public String toString() {
return "BaseMessage [ToUserName=" + ToUserName + ", FromUserName=" + FromUserName + ", CreateTime=" + CreateTime
+ ", MsgType=" + MsgType + ", FuncFlag=" + FuncFlag + "]";
}
}
文本消息
package com.SpringMVC.web.controller;
import com.thoughtworks.xstream.annotations.XStreamAlias;
/**
* 消息响应之文本消息:
* @author 32950745
*
*/
@XStreamAlias("TextMessage")
//@XStreamAlias("xml")
public class TextMessage extends BaseMessage{
// 回复的消息内容
@XStreamAlias("Content")
private String Content;
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
}
@Override
public String toString() {
return super.toString()+"\nTextMessage [Content=" + Content + "]";
}
}
音乐消息
package com.SpringMVC.web.controller;
/**
* 回复消息之音乐消息
* @author 32950745
*
*/
@XStreamAlias("MusicMessage ")
public class MusicMessage extends BaseMessage{
// 回复的消息内容
private String Content;
@XStreamAlias("Content")
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
}
}
图文消息
package com.SpringMVC.web.controller;
/**
* 回复消息之图文消息
* @author 32950745
*
*/
@XStreamAlias("NewsMessage ")
public class NewsMessage extends BaseMessage{
// 回复的消息内容
@XStreamAlias("Content")
private String Content;
public String getContent() {
return Content;
}
public void setContent(String content) {
Content = content;
}
}
/**
* 回复消息之图文消息
* @author 32950745
*
*/
@XStreamAlias("Article")
public class Article extends BaseMessage{
@XStreamAlias("Title")
private String title = "";
@XStreamAlias("Description")
private String description = "";
@XStreamAlias("PicUrl")
private String picUrl = "";
@XStreamAlias("Url")
private String url = "";
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getPicUrl() {
return picUrl;
}
public void setPicUrl(String picUrl) {
this.picUrl = picUrl;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
token验证
package com.SpringMVC.web.controller;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 请求校验工具类
* @author 32950745
*
*/
public class CheckoutUtil {
// 与接口配置信息中的Token要一致
private static String token = "Javen";
/**
* 验证签名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
// Arrays.sort(arr);
sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
public static void sort(String a[]) {
for (int i = 0; i < a.length - 1; i++) {
for (int j = i + 1; j < a.length; j++) {
if (a[j].compareTo(a[i]) < 0) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
}