微信開放平臺之公衆號第三方平臺開發及全網發佈驗證

    技術交流請加QQ羣:Jeewx微信開發④【289709451】
css

  微信公衆號第三方平臺的開放,讓公衆號運營者在面向垂直行業需求時,能夠經過一鍵登陸受權給第三方開發者,來完成相關的處理能力,方便快捷,那如何才能開發出一個公衆號第三方平臺供一鍵受權呢?本文以JAVA做爲後臺服務的實現語言,實現了微信第三方開放平臺開發所須要的主要業務流程,並針對全網發佈的檢測作了相應的代碼處理,以經過微信全網檢測,能夠接入任意的微信公衆號。api

根據微信第三方平臺的審覈需求,你須要在微信開放平臺上註冊第三方平臺信息時,提供以下幾個主要的服務:

41.png


42.png


一、 受權事件接收服務 ,對應填寫的審覈資料中受權事件接收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);
    }


以上是開發微信第三方開發平臺的主要服務代碼,想要經過微信全網接入檢測併成功發佈,還有以下的工做的須要作:
  • 開發一個體驗頁,能夠直接讓審覈人員體驗,由於須要的是直接體驗,因此訪問該頁面就不要有認證和權限控制之類的邏輯了,這個頁面要求符合微信第三方平臺基本的設計要求,本人簡單實現了以下的頁面格式是能夠成功經過審覈的,以下:

43.png

 
  • 針對微信全網檢測的固定帳號作出特定的響應,主要包括一個文本消息響應,一個事件消息響應和一個客服接口調用驗證,微信全網檢測要求測試的固定帳號接收到以上消息後,分別作出以下的響應:接收到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這兩個文件。
相關文章
相關標籤/搜索