目前网上有很多,一分钟微信公众平台教程,但很少有快速搭建微信公众平台服务器教程。
本文以fastweixin框架作为基础,以微信公众平台企业号作为目标,快速搭建微信公众平台服务器。
本文目标:
1.极其方便的实现微信公众平台服务端开发
2、完成服务器绑定
3、实现用户消息监听
本文技术要求:要求大家熟悉JAVA基本语言和servlet
很多大学同学可能对servlet不太熟悉,推荐大家学习书:《head first servlets and jsp》,除此之外不要求swing awt这些知识。
首先这个框架已经为我们实现基本结构,但本人从实践中发现里面有很多需要自己扩充修改BUG的地方,本人使用完全自己实现自己的WeixinServletSupport方式。
0、修改JDK:
微信企业号均使用消息安全模式,安全模式都是AES加密,AES是米国政府采用的加密,所以强加密算法在早期是出口限制,同时在 jdk中有所限制,导致想使用安全模式,必须修改jdk内部的jar包替换为 JCE Unlimited Strength。
否则你会收到异常java.security.InvalidKeyException:illegal Key Size
下面代码修改自WeixinServletSupport
说明:
1、里面有个MyMessageUtil,后续文章会讲述,修改适应weblogic的,如果不需要可以换回原框架MessageUtil
2、用户发来所有信息都可以处理,可以自行修改代码。
3、bindServer进行修改处理判断没有参数调用情况。
package com.luozhuang;
import com.github.sd4324530.fastweixin.company.handle.QYEventHandle;
import com.github.sd4324530.fastweixin.company.handle.QYMessageHandle;
import com.github.sd4324530.fastweixin.company.message.req.QYBaseEvent;
import com.github.sd4324530.fastweixin.company.message.req.QYBaseReq;
import com.github.sd4324530.fastweixin.company.message.req.QYBaseReqMsg;
import com.github.sd4324530.fastweixin.company.message.req.QYBatchJobEvent;
import com.github.sd4324530.fastweixin.company.message.req.QYEnterAgentEvent;
import com.github.sd4324530.fastweixin.company.message.req.QYEventType;
import com.github.sd4324530.fastweixin.company.message.req.QYImageReqMsg;
import com.github.sd4324530.fastweixin.company.message.req.QYLocationEvent;
import com.github.sd4324530.fastweixin.company.message.req.QYLocationReqMsg;
import com.github.sd4324530.fastweixin.company.message.req.QYMenuEvent;
import com.github.sd4324530.fastweixin.company.message.req.QYReqType;
import com.github.sd4324530.fastweixin.company.message.req.QYScanCodeEvent;
import com.github.sd4324530.fastweixin.company.message.req.QYSendPicInfoEvent;
import com.github.sd4324530.fastweixin.company.message.req.QYTextReqMsg;
import com.github.sd4324530.fastweixin.company.message.req.QYVideoReqMsg;
import com.github.sd4324530.fastweixin.company.message.req.QYVoiceReqMsg;
import com.github.sd4324530.fastweixin.company.message.resp.QYBaseRespMsg;
import com.github.sd4324530.fastweixin.company.message.resp.QYTextRespMsg;
import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.github.sd4324530.fastweixin.servlet.QYWeixinSupport;
import com.github.sd4324530.fastweixin.util.BeanUtil;
import com.github.sd4324530.fastweixin.util.CollectionUtil;
import com.github.sd4324530.fastweixin.util.StrUtil;
import com.fastwixinextend.MyMessageUtil;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MainServernSupport {
private static final Logger LOG = LoggerFactory.getLogger(QYWeixinSupport.class);
private static final Object LOCK = new Object();
protected String fromUserName, toUserName;
/**
* 微信消息处理器列表
*/
private static List<QYMessageHandle> messageHandles;
/**
* 子类重写,加入自定义的微信消息处理器,细化消息的处理
*
* @return 微信消息处理器列表
*/
protected List<QYMessageHandle> initMessageHandles() {
return null;
}
/**
* 子类重写,加入自定义的微信事件处理器,细化消息的处理
*
* @return 微信事件处理器列表
*/
protected List<QYEventHandle> initEventHandles() {
return null;
}
/**
* 微信事件处理器列表
*/
private static List<QYEventHandle> eventHandles;
public MainServernSupport() {
super();
}
/**
* @return 你的回调token
*/
public static String getToken() {
return "luozhuang";
}
/**
* @return "这里填写你的企业corpid"
*/
public static String getCropId() {
return "luozhuang";
}
public static String getAgentID() {
return "luozhuang";
}
/**
* @return "你的回调encodingAesKey "
*/
public static String getAESKey() {
return "luozhuang";
}
public static String getAPPSecret() {
return "luozhuang";
}
/**
* 绑定服务器的方法
* @param request
* @param response
*/
public void bindServer(HttpServletRequest request,
HttpServletResponse response) {
PrintWriter pw = null;
try {
pw = response.getWriter();
} catch (IOException e) {
LOG.debug(e);
}
if (StrUtil.isBlank(getToken()) || StrUtil.isBlank(getAESKey()) ||
StrUtil.isBlank(getCropId())) {
pw.write("成功");
pw.flush();
pw.close();
return;
}
if (StrUtil.isBlank(request.getParameter("echostr")) || StrUtil.isBlank(request.getParameter("msg_signature")) || StrUtil.isBlank(request.getParameter("timestamp")) ||
StrUtil.isBlank(request.getParameter("nonce"))) {
pw.write("成功");
pw.flush();
pw.close();
return;
}
try {
WXBizMsgCrypt pc =
new WXBizMsgCrypt(getToken(), getAESKey(), getCropId());
String echoStr =
pc.VerifyURL(request.getParameter("msg_signature"),
request.getParameter("timestamp"),
request.getParameter("nonce"),
request.getParameter("echostr"));
pw.write(echoStr);
pw.flush();
pw.close();
} catch (AesException e) {
if (StrUtil.isNotBlank(e.OrginexceptionMessage)) {
LOG.debug(e.OrginexceptionMessage);
}
LOG.debug("msg_signature",request.getParameter("msg_signature"));
LOG.debug("timestamp",request.getParameter("timestamp"));
LOG.debug("nonce",request.getParameter("nonce"));
LOG.debug("echostr",request.getParameter("echostr"));
LOG.debug(e);
}
finally
{
pw.write("");
pw.flush();
pw.close();
}
}
/**
* 处理微信服务器发来的请求方法
*
* @param request
* http请求对象
* @return 处理消息的结果,已经是接口要求的XML的报文了
*/
public String processRequest(HttpServletRequest request) {
try {
Map<String, Object> reqMap = MyMessageUtil.parseXml(request, getToken(), getCropId(), getAESKey());
fromUserName = (String) reqMap.get("FromUserName");
toUserName = (String) reqMap.get("ToUserName");
String result = ProcessMessage(request, reqMap);
return result;
} catch (Exception e) {
LOG.debug(e);
}
return null;
}
/**
* 处理微信传来信息
*
* @param request
* @param reqMap
* @return
*/
public String ProcessMessage(HttpServletRequest request, Map<String, Object> reqMap) {
String msgType = (String) reqMap.get("MsgType");
LOG.debug("收到消息,消息类型:{}", msgType);
QYBaseRespMsg msg = null;
/// BUG修改:equalsIgnoreCase代替equals,因为微信传来的Type不分大小写
if (msgType.equalsIgnoreCase(QYReqType.EVENT)) {
String eventType = (String) reqMap.get("Event");
if (QYEventType.SUBSCRIBE.equals(eventType)) {
QYBaseEvent event = new QYBaseEvent();
buildBasicEvent(reqMap, event);
msg = handleSubScribe(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
} else if (QYEventType.UNSUBSCRIBE.equalsIgnoreCase(eventType)) {
QYBaseEvent event = new QYBaseEvent();
buildBasicEvent(reqMap, event);
msg = handleUnsubscribe(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
} else if (QYEventType.CLICK.equalsIgnoreCase(eventType)) {
String eventKey = (String) reqMap.get("EventKey");
LOG.debug("eventKey:{}", eventKey);
QYMenuEvent event = new QYMenuEvent(eventKey);
buildBasicEvent(reqMap, event);
msg = handleMenuClickEvent(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
} else if (QYEventType.VIEW.equalsIgnoreCase(eventType)) {
String eventKey = (String) reqMap.get("EventKey");
LOG.debug("eventKey:{}", eventKey);
QYMenuEvent event = new QYMenuEvent(eventKey);
buildBasicEvent(reqMap, event);
msg = handleMenuViewEvent(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
} else if (QYEventType.LOCATION.equalsIgnoreCase(eventType)) {
double latitude = Double.parseDouble((String) reqMap.get("Latitude"));
double longitude = Double.parseDouble((String) reqMap.get("Longitude"));
double precision = Double.parseDouble((String) reqMap.get("Precision"));
QYLocationEvent event = new QYLocationEvent(latitude, longitude, precision);
buildBasicEvent(reqMap, event);
msg = handleLocationEvent(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
} else if (QYEventType.SCANCODEPUSH.equalsIgnoreCase(eventType)
|| QYEventType.SCANCODEWAITMSG.equalsIgnoreCase(eventType)) {
String eventKey = (String) reqMap.get("EventKey");
Map<String, Object> scanCodeInfo = (Map<String, Object>) reqMap.get("ScanCodeInfo");
String scanType = (String) scanCodeInfo.get("ScanType");
String scanResult = (String) scanCodeInfo.get("ScanResult");
QYScanCodeEvent event = new QYScanCodeEvent(eventKey, scanType, scanResult);
buildBasicEvent(reqMap, event);
msg = handleScanCodeEvent(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
} else if (QYEventType.PICPHOTOORALBUM.equalsIgnoreCase(eventType)
|| QYEventType.PICSYSPHOTO.equalsIgnoreCase(eventType)
|| QYEventType.PICWEIXIN.equalsIgnoreCase(eventType)) {
String eventKey = (String) reqMap.get("EventKey");
Map<String, Object> sendPicsInfo = (Map<String, Object>) reqMap.get("SendPicsInfo");
int count = Integer.parseInt((String) sendPicsInfo.get("Count"));
List<Map> picList = (List) sendPicsInfo.get("PicList");
QYSendPicInfoEvent event = new QYSendPicInfoEvent(eventKey, count, picList);
buildBasicEvent(reqMap, event);
msg = handleSendPicsInfoEvent(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
} else if (QYEventType.ENTERAGENT.equalsIgnoreCase(eventType)) {
QYEnterAgentEvent event = new QYEnterAgentEvent();
buildBasicEvent(reqMap, event);
msg = handleEnterAgentEvent(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
} else if (QYEventType.BATCHJOBRESULT.equalsIgnoreCase(eventType)) {
Map<String, Object> batchJob = (Map<String, Object>) reqMap.get("BatchJob");
String jobId = (String) batchJob.get("JobId");
String jobType = (String) batchJob.get("JobType");
int errCode = (Integer) batchJob.get("ErrCode");
String errMsg = (String) batchJob.get("ErrMsg");
QYBatchJobEvent event = new QYBatchJobEvent(jobId, jobType, errCode, errMsg);
buildBasicEvent(reqMap, event);
msg = handleBatchJobEvent(event);
if (BeanUtil.isNull(msg)) {
msg = processEventHandle(event);
}
}
} else {
if (QYReqType.TEXT.equalsIgnoreCase(msgType)) {
String content = (String) reqMap.get("Content");
LOG.debug("文本消息内容:{}", content);
QYTextReqMsg textReqMsg = new QYTextReqMsg(content);
buildBasicReqMsg(reqMap, textReqMsg);
msg = handleTextMsg(textReqMsg);
if (BeanUtil.isNull(msg)) {
msg = processMessageHandle(textReqMsg);
}
} else if (QYReqType.IMAGE.equalsIgnoreCase(msgType)) {
String picUrl = (String) reqMap.get("PicUrl");
String mediaId = (String) reqMap.get("MediaId");
QYImageReqMsg imageReqMsg = new QYImageReqMsg(picUrl, mediaId);
buildBasicReqMsg(reqMap, imageReqMsg);
msg = handleImageMsg(imageReqMsg);
if (BeanUtil.isNull(msg)) {
msg = processMessageHandle(imageReqMsg);
}
} else if (QYReqType.VOICE.equalsIgnoreCase(msgType)) {
String format = (String) reqMap.get("Format");
String mediaId = (String) reqMap.get("MediaId");
QYVoiceReqMsg voiceReqMsg = new QYVoiceReqMsg(mediaId, format);
buildBasicReqMsg(reqMap, voiceReqMsg);
msg = handleVoiceMsg(voiceReqMsg);
if (BeanUtil.isNull(msg)) {
msg = processMessageHandle(voiceReqMsg);
}
} else if (QYReqType.VIDEO.equalsIgnoreCase(msgType)) {
String thumbMediaId = (String) reqMap.get("ThumbMediaId");
String mediaId = (String) reqMap.get("MediaId");
QYVideoReqMsg videoReqMsg = new QYVideoReqMsg(mediaId, thumbMediaId);
buildBasicReqMsg(reqMap, videoReqMsg);
msg = handleVideoMsg(videoReqMsg);
if (BeanUtil.isNull(msg)) {
msg = processMessageHandle(videoReqMsg);
}
} else if (QYReqType.SHORT_VIDEO.equalsIgnoreCase(msgType)) {
String thumbMediaId = (String) reqMap.get("ThumbMediaId");
String mediaId = (String) reqMap.get("MediaId");
QYVideoReqMsg videoReqMsg = new QYVideoReqMsg(mediaId, thumbMediaId);
buildBasicReqMsg(reqMap, videoReqMsg);
msg = handleShortVideoMsg(videoReqMsg);
if (BeanUtil.isNull(msg)) {
msg = processMessageHandle(videoReqMsg);
}
} else if (QYReqType.LOCATION.equalsIgnoreCase(msgType)) {
double locationX = Double.parseDouble((String) reqMap.get("Location_X"));
double locationY = Double.parseDouble((String) reqMap.get("Location_Y"));
// BUG:Scale是首字母大写
int scale = Integer.parseInt((String) reqMap.get("Scale"));
String label = (String) reqMap.get("Label");
// BUG:locationY 写成locationX
QYLocationReqMsg locationReqMsg = new QYLocationReqMsg(locationX, locationY, scale, label);
buildBasicReqMsg(reqMap, locationReqMsg);
msg = handleLocationMsg(locationReqMsg);
if (BeanUtil.isNull(msg)) {
msg = processMessageHandle(locationReqMsg);
}
}
}
String result = "";
if (BeanUtil.nonNull(msg)) {
msg.setFromUserName(toUserName);
msg.setToUserName(fromUserName);
result = msg.toXml();
/// BUG:缺少这个标签
result = "<xml>" + result + "</xml>";
LOG.debug("回复内容:{}", result);
try {
WXBizMsgCrypt pc = new WXBizMsgCrypt(getToken(), getAESKey(), getCropId());
result = pc.EncryptMsg(result, request.getParameter("timestamp"), request.getParameter("nonce"));
LOG.debug("加密后密文:{}", result);
} catch (AesException e) {
LOG.error("加密异常", e);
}
}
return result;
}
/**
* 处理文本消息,有需要时子类重写
*
* @param msg
* 请求消息对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleTextMsg(QYTextReqMsg msg) {
return new QYTextRespMsg("感谢您发送文本");
}
/**
* 处理图片消息,有需要时子类重写
*
* @param msg
* 请求消息对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleImageMsg(QYImageReqMsg msg) {
return new QYTextRespMsg("感谢您上报图片");
}
/**
* 处理语音消息,有需要时子类重写
*
* @param msg
* 请求消息对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleVoiceMsg(QYVoiceReqMsg msg) {
return new QYTextRespMsg("感谢您上报语音");
}
/**
* 处理视频消息,有需要时子类重写
*
* @param msg
* 请求消息对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleVideoMsg(QYVideoReqMsg msg) {
return new QYTextRespMsg("感谢您上报视频");
}
/**
* 处理小视频消息,有需要时子类重写
*
* @param msg
* 请求消息对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleShortVideoMsg(QYVideoReqMsg msg) {
return new QYTextRespMsg("感谢您上报视频");
}
/**
* 处理地理位置消息,有需要时子类重写
*
* @param msg
* 请求消息对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleLocationMsg(QYLocationReqMsg msg) {
return new QYTextRespMsg("感谢您上报地理位置");
}
/**
* 处理地理位置事件,有需要时子类重写
*
* @param event
* 地理位置事件对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleLocationEvent(QYLocationEvent event) {
return new QYTextRespMsg("感谢您上报地理位置");
}
/**
* 处理菜单点击事件,有需要时子类重写
*
* @param event
* 菜单点击事件对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleMenuClickEvent(QYMenuEvent event) {
String eventKey = event.getEventKey();
QYTextRespMsg textMessage = new QYTextRespMsg();
// 根据key值判断用户点击的按钮
if (eventKey.equals("登录")) {
textMessage
.setContentBuilder("登录");
}
return textMessage;
}
/**
* 处理菜单跳转事件,有需要时子类重写
*
* @param event
* 菜单跳转事件对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleMenuViewEvent(QYMenuEvent event) {
return new QYTextRespMsg("感谢菜单跳转");
}
/**
* 处理菜单扫描推事件,有需要时子类重写
*
* @param event
* @return
*/
protected QYBaseRespMsg handleScanCodeEvent(QYScanCodeEvent event) {
return new QYTextRespMsg("感谢您扫描二维码");
}
/**
* 处理菜单弹出相册事件,有需要时子类重写
*
* @param event
* 菜单弹出相册事件
* @return 响应的消息对象
*/
protected QYBaseRespMsg handleSendPicsInfoEvent(QYSendPicInfoEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理用户进入应用事件,有需要时子类重写
*
* @param event
* 添加应用事件对象
* @return 响应的消息对象
*/
protected QYBaseRespMsg handleEnterAgentEvent(QYEnterAgentEvent event) {
return new QYTextRespMsg("感谢进入应用");
}
/**
* 处理异步任务通知事件,有需要时子类重写
*
* @param event
* 添加通知事件对象
* @return 响应的消息对象
*/
protected QYBaseRespMsg handleBatchJobEvent(QYBatchJobEvent event) {
return handleDefaultEvent(event);
}
/**
* 处理添加关注事件,有需要时子类重写
*
* @param event
* 添加关注事件对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleSubScribe(QYBaseEvent event) {
return new QYTextRespMsg("感谢您的关注");
}
/**
* 处理取消关注事件,有需要时子类重写
*
* @param event
* 取消关注事件对象
* @return 响应消息对象
*/
protected QYBaseRespMsg handleUnsubscribe(QYBaseEvent event) {
return null;
}
protected QYBaseRespMsg handleDefaultMsg(QYBaseReqMsg msg) {
return null;
}
protected QYBaseRespMsg handleDefaultEvent(QYBaseEvent event) {
return null;
}
/**
* 自定义的消息事件处理
*
* @param msg
* 微信消息
* @return
*/
private QYBaseRespMsg processMessageHandle(QYBaseReqMsg msg) {
if (CollectionUtil.isEmpty(messageHandles)) {
synchronized (LOCK) {
messageHandles = this.initMessageHandles();
}
}
if (CollectionUtil.isNotEmpty(messageHandles)) {
for (QYMessageHandle messageHandle : messageHandles) {
QYBaseRespMsg resultMsg = null;
boolean result;
try {
result = messageHandle.beforeHandle(msg);
} catch (Exception e) {
result = false;
}
if (result) {
resultMsg = messageHandle.handle(msg);
}
if (BeanUtil.nonNull(resultMsg)) {
return resultMsg;
}
}
}
return null;
}
/**
* 自定义的消息事件处理
*
* @param event
* @return
*/
private QYBaseRespMsg processEventHandle(QYBaseEvent event) {
if (CollectionUtil.isEmpty(eventHandles)) {
synchronized (LOCK) {
eventHandles = this.initEventHandles();
}
}
if (CollectionUtil.isNotEmpty(eventHandles)) {
for (QYEventHandle eventHandle : eventHandles) {
QYBaseRespMsg resultMsg = null;
boolean result;
try {
result = eventHandle.beforeHandle(event);
} catch (Exception e) {
result = false;
}
if (result) {
resultMsg = eventHandle.handle(event);
}
if (BeanUtil.nonNull(resultMsg)) {
return resultMsg;
}
}
}
return null;
}
private void buildBasicReqMsg(Map<String, Object> reqMap, QYBaseReqMsg reqMsg) {
addBasicReqParams(reqMap, reqMsg);
reqMsg.setMsgId((String) reqMap.get("MsgId"));
}
private void buildBasicEvent(Map<String, Object> reqMap, QYBaseEvent event) {
addBasicReqParams(reqMap, event);
event.setEvent((String) reqMap.get("Event"));
}
private void addBasicReqParams(Map<String, Object> reqMap, QYBaseReq req) {
req.setMsgType((String) reqMap.get("MsgType"));
req.setFromUserName((String) reqMap.get("FromUserName"));
req.setToUserName((String) reqMap.get("ToUserName"));
req.setCreateTime(Long.parseLong((String) reqMap.get("CreateTime")));
}
}
package com.luozhuang;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 请求处理的核心类
*
*/
public class CoreServletDemo extends HttpServlet {
private static final long serialVersionUID = 4440739483644821986L;
MainServernSupport support = new MainServernSupport();
public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
support.bindServer(request, response);
}
/**
* 重写servlet中的post方法,用于接收微信服务器发来的消息,置为final方法,用户已经无需重写这个方法啦
*
* @param request http请求对象
* @param response http响应对象
* @throws ServletException servlet异常
* @throws IOException IO异常
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");
response.setCharacterEncoding("UTF-8");
String resp = support.processRequest(request);
PrintWriter out = response.getWriter();
out.print(resp);
out.close();
}
}