微信开放平台之公众号第三方平台开发及全网发布验证

来源:互联网 发布:淘宝外卖怎么找不到了 编辑:程序博客网 时间:2024/04/28 16:51

微信公众号第三方平台的开放,让公众号运营者在面向垂直行业需求时,可以通过一键登录授权给第三方开发者,来完成相关的处理能力,方便快捷,那如何才能开发出一个公众号第三方平台供一键授权呢?本文以JAVA作为后台服务的实现语言,实现了微信第三方开放平台开发所需要的主要业务流程,并针对全网发布的检测做了相应的代码处理,以通过微信全网检测,可以接入任意的微信公众号。
根据微信第三方平台的审核需求,你需要在微信开放平台上注册第三方平台信息时,提供如下几个主要的服务:
这里写图片描述
这里写图片描述

1、授权事件接收服务,对应填写的审核资料中授权事件接收URL,微信会将相关的授权事件信息推送到该REST服务上,推送的主要消息包括验证票据ComponentVerifyTicket和取消授权的公众号AuthorizerAppid,该服务需要对微信推送过来的该类消息立即做出回应并返回success内容,该服务事件的JAVA实现方式如下:

  • 123456789101112131415
      /**     * 授权事件接收     *      * @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"); // 输出响应的内容。    }

    更具体的实现代码如下:

    123456789101112131415161718192021222324252627282930313233
      /**     * 处理授权事件的推送     *      * @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);        }    }

2、公众号消息与事件接收服务,对应填写的审核资料中公众号消息与事件接收URL,微信会将粉丝发送给公众号的消息和事件推送到该REST服务上,微信公众平台要求该消息和事件接收服务在5秒内做出回应,如果5秒内微信公众平台得不到响应消息,粉丝将将收到提示公众号暂时服务提供服务的错误信息。对于需要对粉丝发送的消息走人工渠道做出响应的公众号来说,此时就需要首先接收下消息,将消息交给后来逻辑转人工处理,然后立即以空格消息响应微信公众平台,微信收到空格消息后就会知道该粉丝发送的消息已经被妥善处理,并对该响应不做任何处理,同时不会发起消息重新推送的重试。该服务的JAVA实现实现方式如下:

  • 123456789101112131415161718192021222324252627282930
     /**     * 公众号消息与事件接收     *      * @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();         WeixinOpenService.getInstance().processMessageAndEvent(request, xml);        WeixinOpenService.getInstance().output(response, "");    }
    123456789101112131415
     /**     * 处理微信推送过来的授权公众号的消息及事件     *      */    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);    }

以上是开发微信第三方开发平台的主要服务代码,想要通过微信全网接入检测并成功发布,还有如下的工作的需要做:

1、开发一个体验页,可以直接让审核人员体验,因为需要的是直接体验,所以访问该页面就不要有认证和权限控制之类的逻辑了,这个页面要求符合微信第三方平台基本的设计要求,本人简单实现了如下的页面格式是可以成功通过审核的,如下:这里写图片描述

2、针对微信全网检测的固定账号做出特定的响应,主要包括一个文本消息响应,一个事件消息响应和一个客服接口调用验证,微信全网检测要求测试的固定账号接收到以上消息后,分别做出如下的响应:接收到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后台实现代码如下:

123456789101112131415161718192021222324252627282930313233343536
 /**     * 公众号消息与事件接收     *      * @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这个账号是微信全网接入检测的固定账号,针对全网检测需要对该账号做特出响应,一旦全网接入检测通过,这部分的代码是可以去掉的,只有全网检测的时候才需要这部分代码。

1234567891011121314151617181920212223242526272829
 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;        }    }

根据消息或事件类型区分后,剩余的逻辑只需要处理成对应的回复内容即可,如下:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
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-bin/message/custom/send?access_token=" + 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这两个文件。

本文章版权归环信所有,转载请注明出处。更多技术文章请访问http://blog.easemob.com/
阅读全文
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 黄香 黄馨 小黄鱼 黄鱼 黄鱼做法 小黄鱼做法 黄鱼的做法 焖小黄鱼 红烧黄鱼 红烧小黄鱼 黄鱼网站 黄鱼图片 黄鱼veda 清蒸黄鱼 清蒸小黄鱼 小黄鱼图片 家常焖黄鱼 家常烧黄鱼 黄鱼汤 香煎小黄鱼 灌汤黄鱼 干煎小黄鱼 椒香小黄鱼 炸黄鱼做法 油炸黄鱼 煎小黄鱼 香酥小黄鱼 香煎黄鱼 黄鱼面 黄鱼鲞 黄鱼视屏 蒜烧小黄鱼 黄鱼影院 黄鱼力推荐 蒸黄鱼 炸黄鱼 干烧黄鱼 黄鱼推荐 黄鱼价格 野生黄鱼 一品蒸黄鱼