在微信第三方平台方创建成功并最终开发测试完毕,提交全网发布申请时,微信服务器会通过自动化测试的方式,检测服务的基础逻辑是否可用,在确保基础可用的情况下,才会允许公众号第三方平台提交全网发布,全网发布的逻辑比较简单,官网说得也很清楚,如果逻辑没问题的小伙伴还是失败,那就可能是加解密的时候参数写错了,一定要仔细排查。
配置:
接收ticket的地址:http:xxxx.cn /common/ticket
接收消息和事件的地址:http:xxxx.cn /common/mp/{APPID}
这两个地址只要在第三方平台配置时和项目保持统一即可。
以下为具体代码:
@RequestMapping(value = "/common/ticket", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
public @ResponseBody String ticket(HttpServletRequest req, String authorization_code,String entCode,HttpServletResponse resp) {
logger.info("接收到微信传来的ticket数据流...");
String msgSignature = req.getParameter("msg_signature");
String timestamp = req.getParameter("timestamp");
String nonce = req.getParameter("nonce");
try {
BufferedReader br = new BufferedReader(new InputStreamReader(req.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
String encStr = sb.toString();
if(logger.isDebugEnabled()){
logger.debug("encStr: " + encStr);
}
if (encStr != null) {
WXBizMsgCrypt pc = new WXBizMsgCrypt(MpParams.WX_OPEN_TOKEN, MpParams.WX_OPEN_AESKEY, MpParams.WX_OPEN_APPID); // 构造加解密参数
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(encStr);
InputSource is = new InputSource(sr);
Document document = db.parse(is);
Element root = document.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
String encrypt = nodelist1.item(0).getTextContent();
if(logger.isDebugEnabled()){
logger.debug("encrypt: " + encrypt);
}
String format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
String fromXML = String.format(format, encrypt);
String resultXml = pc.decryptMsg(msgSignature, timestamp,nonce, fromXML); //检验消息的真实性,并且获取解密后的明文
if(StringUtils.isNotEmpty(xmlMap.get("InfoType")) && "component_verify_ticket".equals(xmlMap.get("InfoType"))){
String componentVerifyTicket = xmlMap.get("ComponentVerifyTicket");
logger.info("微信服务器发送过来的ticket: " + componentVerifyTicket);
}
} catch (Exception e) {
String str = "解析微信服务器发送的消息流失败...";
logger.error(str, e);
}
return "success";
}
由于ticket在我们的项目中比较有用,所以会解密,如果只是为了全网发布,方法体中只需要一句话:return “success”;
/**
* @Description 消息接口
* @author mlingwei mlingwei@foxmail.com
*/
@RequestMapping(value = " /common/mp/{APPID}", method = RequestMethod.POST, produces = "text/html;charset=UTF-8")
public void wxmessage(@PathVariable("APPID")String APPID, HttpServletRequest req, HttpServletResponse resp) {
String msgSignature = req.getParameter("msg_signature");
String timestamp = req.getParameter("timestamp");
String nonce = req.getParameter("nonce");
//开始解密
BufferedReader br = new BufferedReader(new InputStreamReader(req.getInputStream()));
String line = null;
StringBuilder sb = new StringBuilder();
while ((line = br.readLine()) != null) {
sb.append(line);
}
br.close();
String encStr = sb.toString();
if(logger.isDebugEnabled()){
logger.debug("encStr: " + encStr);
}
if (encStr != null) {
WXBizMsgCrypt pc = new WXBizMsgCrypt(MpParams.WX_OPEN_TOKEN, MpParams.WX_OPEN_AESKEY, MpParams.WX_OPEN_APPID); // 构造加解密参数
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(encStr);
InputSource is = new InputSource(sr);
Document document = db.parse(is);
Element root = document.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
String encrypt = nodelist1.item(0).getTextContent();
if(logger.isDebugEnabled()){
logger.debug("encrypt: " + encrypt);
}
String format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%s]]></Encrypt></xml>";
String fromXML = String.format(format, encrypt);
String resultXml = pc.decryptMsg(msgSignature, timestamp,nonce, fromXML); //检验消息的真实性,并且获取解密后的明文
//判断消息类型
// 将xml转为map对象
Map<String, String> map = MessageUtils.xmlToMap(resultXml);
String toUserName = map.get("ToUserName"); // 开发者微信号
String fromUserName = map.get("FromUserName"); // 发送方帐号(一个OpenID)
String createTime = map.get("CreateTime"); // 消息创建时间 (整型)
String msgType = map.get("MsgType"); // text
String content = map.get("Content"); // 文本消息内容,文本换行 ‘\n’
String msgId = map.get("MsgId"); // 消息id,64位整型
String event = map.get("Event"); // 事件类型,如CLICK
String eventKey = map.get("EventKey"); // 事件KEY值,与自定义菜单接口中KEY值对应
if(APPID.equals("wx570bc396a51b8ff8")){
if("text".equals(msgType)){
if("TESTCOMPONENT_MSG_TYPE_TEXT".equals(content)){
String returnContent = content+"_callback";
replyTextMessage(request,response,returnContent,toUserName,fromUserName,appid);
}else if(StringUtils.startsWithIgnoreCase(content, "QUERY_AUTH_CODE")){
output(response, "");
//接下来客服API再回复一次消息
replyApiTextMessage(request,response,content.split(":")[1],fromUserName,appid);
}
}
//如果是事件消息
if("event").equals(msgType)){
event = event + "from_callback";
replyTextMessage(req, resp, event, toUserName, fromUserName, nonce);
}
}
//回复文本消息
/**
* 回复微信服务器"文本消息"
* @param request
* @param response
* @param content
* @param toUserName
* @param fromUserName
* @throws DocumentException
* @throws IOException
*/
public String replyTextMessage(HttpServletRequest request, HttpServletResponse response, String content, String toUserName, String fromUserName, String nonce) throws DocumentException, IOException {
Long createTime = Calendar.getInstance().getTimeInMillis() / 1000;
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
sb.append("<ToUserName><![CDATA["+fromUserName+"]]></ToUserName>");
sb.append("<FromUserName><![CDATA["+toUserName+"]]></FromUserName>");
sb.append("<CreateTime>"+createTime+"</CreateTime>");
sb.append("<MsgType><![CDATA[text]]></MsgType>");
sb.append("<Content><![CDATA["+content+"]]></Content>");
sb.append("</xml>");
String replyMsg = sb.toString();
String returnvaleue = "";
try {
WXBizMsgCrypt pc = new WXBizMsgCrypt(MpParams.WX_OPEN_TOKEN, MpParams.WX_OPEN_AESKEY, MpParams.WX_OPEN_APPID);
returnvaleue = pc.encryptMsg(replyMsg, createTime.toString(), nonce);
} catch (AesException e) {
e.printStackTrace();
}
// response.reset();
output(response, returnvaleue);
return returnvaleue;
}
//回复API文本消息
public void replyApiTextMessage(HttpServletRequest request, HttpServletResponse response, String auth_code, String fromUserName, String appid) throws DocumentException, IOException {
JSONObject getTokenPost = new JSONObject();
getTokenPost.accumulate("component_appid", MpParams.WX_OPEN_APPID);
getTokenPost.accumulate("authorization_code", auth_code);
String getToken = "";
try {
getToken = HttpClientUtil.post(
"https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token="
+ component_access_token, getTokenPost.toString());
} catch (IOException e) {
logger.error("调用获取authorizer_access_token和authorizer_refresh_token接口失败",e);
e.printStackTrace();
}
JSONObject getTokenJson = JSONObject.fromObject(getToken);
JSONObject authorization_info = getTokenJson.getJSONObject("authorization_info");
//authorizer_access_token
String authorizer_access_token = authorization_info.getString("authorizer_access_token");
//调用客服接口
String msg = auth_code + "_from_api";
JSONObject jsonObject = new JSONObject();
jsonObject.put("touser", fromUserName);
jsonObject.put("msgtype", "text");
JSONObject text = new JSONObject();
text.put("content", msg);
jsonObject.put("text", text);
String getInfo = "";
try {
getInfo = HttpClientUtil.post(
"https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="
+ authorizer_access_token, jsonObject.toString());
} catch (IOException e) {
logger.error("调用获取公众号基本信息接口失败",e);
e.printStackTrace();
}
logger.info("发送客服消息结果为:" + getInfo);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 工具类:回复微信服务器"文本消息"
* @param response
* @param returnvaleue
*/
public void output(HttpServletResponse response,String returnvaleue){
try {
PrintWriter pw = response.getWriter();
pw.write(returnvaleue);
pw.flush();
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
其中有些工具类直接参考了网络上的代码,全网发布只要思路清晰,仔细阅读文档很容易成功,代码中若有不合理的地方欢迎探讨。