技術交流請加QQ羣:Jeewx微信開發④【289709451】
css
微信公衆號第三方平臺的開放,讓公衆號運營者在面向垂直行業需求時,能夠經過一鍵登陸受權給第三方開發者,來完成相關的處理能力,方便快捷,那如何才能開發出一個公衆號第三方平臺供一鍵受權呢?本文以JAVA做爲後臺服務的實現語言,實現了微信第三方開放平臺開發所須要的主要業務流程,並針對全網發佈的檢測作了相應的代碼處理,以經過微信全網檢測,能夠接入任意的微信公衆號。api
根據微信第三方平臺的審覈需求,你須要在微信開放平臺上註冊第三方平臺信息時,提供以下幾個主要的服務:
一、
受權事件接收服務
,對應填寫的審覈資料中受權事件接收URL,微信會將相關的受權事件信息推送到該REST服務上,推送的主要消息包括驗證票據ComponentVerifyTicket和取消受權的公衆號AuthorizerAppid,該服務須要對微信推送過來的該類消息當即作出迴應並返回success內容,該服務事件的JAVA實現方式以下:
/**
* 受權事件接收
*
* @param request
* @param response
* @throws IOException
* @throws AesException
* @throws DocumentException
*/
@RequestMapping(value = "/open/event/authorize", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
public void acceptAuthorizeEvent(HttpServletRequest request, HttpServletResponse response) throws IOException, AesException, DocumentException {
WeixinOpenService.getInstance().processAuthorizeEvent(request);
WeixinOpenService.getInstance().output(response, "success"); // 輸出響應的內容。
}
更具體的實現代碼以下:
/**
* 處理受權事件的推送
*
* @param request
* @throws IOException
* @throws AesException
* @throws DocumentException
*/
public void processAuthorizeEvent(HttpServletRequest request) throws IOException, DocumentException, AesException {
String token = WeixinOpenService.TOKEN;
String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp");
String signature = request.getParameter("signature");
String msgSignature = request.getParameter("msg_signature");
if (!StringUtils.isNotBlank(msgSignature))
return;// 微信推送給第三方開放平臺的消息必定是加過密的,無消息加密沒法解密消息
boolean isValid = WechatCallbackServiceController.checkSignature(token, signature, timestamp, nonce);
if (isValid) {
StringBuilder sb = new StringBuilder();
BufferedReader in = request.getReader();
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}
String xml = sb.toString();
String encodingAesKey = WeixinOpenService.ENCODINGAESKEY;// 第三方平臺組件加密密鑰
String appId = getAuthorizerAppidFromXml(xml, "authorizationEvent");// 此時加密的xml數據中ToUserName是非加密的,解析xml獲取便可
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appId);
xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml, "AppId");
processAuthorizationEvent(xml);
}
}
二、
公衆號消息與事件接收服務
,對應填寫的審覈資料中公衆號消息與事件接收URL,微信會將粉絲髮送給公衆號的消息和事件推送到該REST服務上,微信公衆平臺要求該消息和事件接收服務在5秒內作出迴應,若是5秒內微信公衆平臺得不到響應消息,粉絲將將收到提示公衆號暫時服務提供服務的錯誤信息。對於須要對粉絲髮送的消息走人工渠道作出響應的公衆號來講,此時就須要首先接收下消息,將消息交給後來邏輯轉人工處理,而後當即以空格消息響應微信公衆平臺,微信收到空格消息後就會知道該粉絲髮送的消息已經被妥善處理,並對該響應不作任何處理,同時不會發起消息從新推送的重試。該服務的JAVA實現實現方式以下:
/**
* 處理微信推送過來的受權公衆號的消息及事件
*
*/
public void processMessageAndEvent(HttpServletRequest request,String xml) throws IOException, AesException, DocumentException {
String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp");
String msgSignature = request.getParameter("msg_signature");
String encodingAesKey = WeixinOpenService.ENCODINGAESKEY;
String token = WeixinOpenService.TOKEN;
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, WeixinOpenService.COMPONENT_APPID);
xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml, "ToUserName");
WechatCallbackServiceController.processMessage(xml);
}
以上是開發微信第三方開發平臺的主要服務代碼,想要經過微信全網接入檢測併成功發佈,還有以下的工做的須要作:
- 開發一個體驗頁,能夠直接讓審覈人員體驗,由於須要的是直接體驗,因此訪問該頁面就不要有認證和權限控制之類的邏輯了,這個頁面要求符合微信第三方平臺基本的設計要求,本人簡單實現了以下的頁面格式是能夠成功經過審覈的,以下:
- 針對微信全網檢測的固定帳號作出特定的響應,主要包括一個文本消息響應,一個事件消息響應和一個客服接口調用驗證,微信全網檢測要求測試的固定帳號接收到以上消息後,分別作出以下的響應:接收到TESTCOMPONENT_MSG_TYPE_TEXT這樣的文本消息當即回覆給粉絲文本內容TESTCOMPONENT_MSG_TYPE_TEXT_callback;接收到事件消息,當即以文本內容的消息格式回覆粉絲內容event + 「from_callback」,其中event須要根據實際內容替換爲具體事件類型;接收到QUERY_AUTH_CODE:query_auth_code 這樣的文本消息,須要當即響應空字符串給微信,以後調用客服接口回覆粉絲文本消息,內容爲:$query_auth_code\$_from_api,其中query_auth_code須要替換爲微信實際推送過來的數據。主要的JAVA後臺實現代碼以下:
/**
* 公衆號消息與事件接收
*
* @param request
* @param response
* @throws DocumentException
* @throws AesException
* @throws IOException
*/
@RequestMapping(value = "/open/{appid}/callback", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.NO_CONTENT)
public void acceptMessageAndEvent(HttpServletRequest request, HttpServletResponse response) throws IOException, AesException, DocumentException {
String msgSignature = request.getParameter("msg_signature");
if (!StringUtils.isNotBlank(msgSignature))
return;// 微信推送給第三方開放平臺的消息必定是加過密的,無消息加密沒法解密消息
StringBuilder sb = new StringBuilder();
BufferedReader in = request.getReader();
String line;
while ((line = in.readLine()) != null) {
sb.append(line);
}
in.close();
String xml = sb.toString();
Document doc = DocumentHelper.parseText(xml);
Element rootElt = doc.getRootElement();
String toUserName = rootElt.elementText("ToUserName");
if (StringUtils.equalsIgnoreCase(toUserName, "gh_3c884a361561")) {// 微信全網測試帳號
WeixinWholeNetworkTestService.getInstance().checkWeixinAllNetworkCheck(request,response,xml);
}else{
WeixinOpenService.getInstance().processMessageAndEvent(request,xml);
WeixinOpenService.getInstance().output(response, "");
}
}
- 其中gh_3c884a361561這個帳號是微信全網接入檢測的固定帳號,針對全網檢測須要對該帳號作特出響應,一旦全網接入檢測經過,這部分的代碼是能夠去掉的,只有全網檢測的時候才須要這部分代碼。
public void checkWeixinAllNetworkCheck(HttpServletRequest request, HttpServletResponse response,String xml) throws DocumentException, IOException, AesException{
String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp");
String msgSignature = request.getParameter("msg_signature");
String encodingAesKey = WeixinOpenService.ENCODINGAESKEY;
String token = WeixinOpenService.TOKEN;
WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, WeixinOpenService.COMPONENT_APPID);
xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml, "ToUserName");
Document doc = DocumentHelper.parseText(xml);
Element rootElt = doc.getRootElement();
String msgType = rootElt.elementText("MsgType");
String toUserName = rootElt.elementText("ToUserName");
String fromUserName = rootElt.elementText("FromUserName");
switch (msgType) {
case "event":
String event = rootElt.elementText("Event");
replyEventMessage(request,response,event,toUserName,fromUserName);
break;
case "text":
String content = rootElt.elementText("Content");
processTextMessage(request,response,content,toUserName,fromUserName);
break;
default:
break;
}
}
根據消息或事件類型區分後,剩餘的邏輯只須要處理成對應的回覆內容便可,以下:
public void replyEventMessage(HttpServletRequest request, HttpServletResponse response, String event, String toUserName, String fromUserName) throws DocumentException, IOException {
String content = event + "from_callback";
replyTextMessage(request,response,content,toUserName,fromUserName);
}
public void processTextMessage(HttpServletRequest request, HttpServletResponse response,String content,String toUserName, String fromUserName) throws IOException, DocumentException{
if("TESTCOMPONENT_MSG_TYPE_TEXT".equals(content)){
String returnContent = content+"_callback";
replyTextMessage(request,response,returnContent,toUserName,fromUserName);
}else if(StringUtils.startsWithIgnoreCase(content, "QUERY_AUTH_CODE")){
WeixinOpenService.getInstance().output(response, "");
//接下來客服API再回復一次消息
replyApiTextMessage(request,response,content.split(":")[1],fromUserName);
}
}
public void replyApiTextMessage(HttpServletRequest request, HttpServletResponse response, String auth_code, String fromUserName) throws DocumentException, IOException {
String authorization_code = auth_code;
// 獲得微信受權成功的消息後,應該馬上進行處理!!相關信息只會在首次受權的時候推送過來
WeixinOpenData weixinOpenData = WeixinOpenService.getInstance().getWeixinOpenData(WeixinOpenService.COMPONENT_APPID);
long accessTokenExpires = weixinOpenData.getAccessTokenExpires();
String componentAccessToken = weixinOpenData.getComponentAccessToken();
String componentVerifyTicket = weixinOpenData.getComponentVerifyTicket();
JSONObject authorizationInfoJson;
if (!this.isExpired(accessTokenExpires)) {
authorizationInfoJson = WeixinOpenService.getInstance().apiQueryAuth(componentAccessToken, WeixinOpenService.COMPONENT_APPID, authorization_code);
} else {
JSONObject accessTokenJson = WeixinOpenService.getInstance().getComponentAccessToken(WeixinOpenService.COMPONENT_APPID, WeixinOpenService.COMPONENT_APPSECRET, componentVerifyTicket);
componentAccessToken = accessTokenJson.getString("component_access_token");
authorizationInfoJson = WeixinOpenService.getInstance().apiQueryAuth(componentAccessToken, WeixinOpenService.COMPONENT_APPID, authorization_code);
}
if (log.isDebugEnabled()) {
log.debug("weixinopen callback authorizationInfo is " + authorizationInfoJson);
}
JSONObject infoJson = authorizationInfoJson.getJSONObject("authorization_info");
String authorizer_access_token = infoJson.getString("authorizer_access_token");
String url = "https://api.weixin.qq.com/cgi- ... ot%3B + authorizer_access_token;
DefaultHttpClient client = new DefaultHttpClient();
enableSSLDefaultHttpClient(client);
HttpPost httpPost = new HttpPost(url);
JSONObject message = processWechatTextMessage(client, httpPost, fromUserName, auth_code + "_from_api");
if(log.isDebugEnabled()){
log.debug("api reply messto to weixin whole network test respose = "+message);
}
}
public void replyTextMessage(HttpServletRequest request, HttpServletResponse response, String content, String toUserName, String fromUserName) throws DocumentException, IOException {
Long createTime = Calendar.getInstance().getTimeInMillis() / 1000;
StringBuffer sb = new StringBuffer();
sb.append("");
sb.append("");
sb.append("");
sb.append("" + createTime + "");
sb.append("");
sb.append("");
sb.append("");
String replyMsg = sb.toString();
String returnvaleue = "";
try {
WXBizMsgCrypt pc = new WXBizMsgCrypt(WeixinOpenService.TOKEN, WeixinOpenService.ENCODINGAESKEY, WeixinOpenService.COMPONENT_APPID);
returnvaleue = pc.encryptMsg(replyMsg, createTime.toString(), "easemob");
} catch (AesException e) {
log.error("auto reply to weixin whole network test occur exception = "+ e);
e.printStackTrace();
}
if(log.isDebugEnabled()){
log.debug("return weixin whole network test Text message is = "+returnvaleue);
}
WeixinOpenService.getInstance().output(response, returnvaleue);
}
以上是微信第三方開放平臺開發主要的業務流程,在實際開發中,還有兩點須要特別注意:
一、微信要求第三方開放平臺必須以密文方式接收消息;
二、在實際部署時,須要更換JAVA安全包相關的內容,不然將出現祕鑰長度不夠的異常,須要替換的文件包括JAVA_HOME/jre/lib/security/local_policy.jar和 JAVA_HOME/jre/lib/security/US_export_policy.jar這兩個文件。