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

来源:互联网 发布:网络销售高仿酒 编辑:程序博客网 时间:2024/04/27 21: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博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 非主流伤感 伤感歌曲 伤感情歌 伤感歌 伤感图片男 唯美伤感 朋友圈伤感 伤感文字控 qq签名伤感 伤感的歌 伤感独白 伤感图片带字 非主流伤感图片 经典语录伤感人生感悟 伤感文字图片 2018伤感图片 孤独一个人伤感照片 落叶一个人伤感背影 伤感情感语录 伤感句子句句心痛 一个人喝酒伤感图片 唯美伤感图片 男生伤感图片 低落伤感图片 朋友圈伤感句子 伤感句子表达心情失落 喝酒伤感图片 喝酒图片伤感 分手伤感句子 男人伤感图片 背影图片伤感 秋天落叶伤感风景图 伤感情歌大全 夜深人静孤独伤感句子 独自一人喝酒伤感照片 非主流图片唯美伤感 伤春悲秋 伤春悲秋完整诗句 悲秋伤春 伤春 伤春悲秋一世情