java微信公众号第三方接入解析及全网检测代码示例

一、接入大体思路解析

这里主要是查看文档的授权流程技术说明,这里从开发者角度介绍。
接入第三方的开发者想必已经对公众号开发比较了解,第三方是代微信号实现功能的一个组件,所以也许微信方的开发们也是如此考虑,为第三方的access_token取名为component_access_token。

1、首先获取第三方

component_access_token
这个过程中会使用到微信服务器定期向我们服务器推送的component_verify_ticket字段,加上component_appid、component_appsecret等信息即可获得。

2、获取授权公众号信息

类似公众号网页授权,通过回调的形式,微信公众号运营者可以通过微信扫码的形式将公众号授权给第三方,唯一有些不同的这里在拼出授权跳转页面url后的第一步是引导用户跳转到该页面上,再进行扫码,所以这个跳转不是必须在微信内置浏览器中进行。
拼出的url中的参数出了要用到component_access_token外还需要一个pre_auth_code预授权码,可通过component_access_token获得,具体的实效时间机制和component_access_token差不多。
用户授权成功后会在redirect_url后接参数auth_code,类似公众号网页授权的code值,通过auth_code和component_access_token等信息即可获得该授权公众号信息,若中间要与公众号第三方平台的账号绑定再带一个账号标示参数即可。

3、带公众号实现业务

公众号授权成功后第三方会得到该公众号的authorizer_access_token,与公众号的access_token功能相同,使用这个即可用微信公众号的接口代公众号实现业务。
有些不同的再第三方文档里以有说明。官方文档
另外文档上没有提到的是,每当有公众号授权成功后,微信服务器会将公众号授权信息推送到授权事件接收URL,即接收component_verify_ticket的接口。

4、关于加密解密

获取微信服务器推送信息的时候都是进行加密的。

二、接入中发现的几个问题

1、微信的示例java代码,XMLParse类中

/**
     * 提取出xml数据包中的加密消息
     * @param xmltext 待提取的xml字符串
     * @return 提取出的加密消息字符串
     * @throws AesException 
     */
    public static Object[] extract(String xmltext) throws AesException     {
        Object[] result = new Object[3];
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xmltext);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);

            Element root = document.getDocumentElement();
            NodeList nodelist1 = root.getElementsByTagName("Encrypt");
            NodeList nodelist2 = root.getElementsByTagName("ToUserName");
            System.out.println("nodelist2.item(0)="+nodelist2.item(0));
            result[0] = 0;
            result[1] = nodelist1.item(0).getTextContent();
            //这里加了一个判断,因为接收推送component_verify_ticket的解谜过程中没有第三个参数,回报空指针异常
            if(nodelist2.item(0) != null){
                result[2] = nodelist2.item(0).getTextContent();
            }
            return result;
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.ParseXmlError);
        }
    }

2、文档中:使用授权码换取公众号的接口调用凭据和授权信息

返回json信息最后少了一个}

{ 
"authorization_info": {
"authorizer_appid": "wxf8b4f85f3a794e77", 
"authorizer_access_token": "QXjUqNqfYVH0yBE1iI_7vuN_9gQbpjfK7hYwJ3P7xOa88a89-Aga5x1NMYJyB8G2yKt1KCl0nPC3W9GJzw0Zzq_dBxc8pxIGUNi_bFes0qM", 
"expires_in": 7200, 
"authorizer_refresh_token": "dTo-YCXPL4llX-u1W1pPpnp8Hgm4wpJtlR6iV0doKdY", 
"func_info": [
{
"funcscope_category": {
"id": 1
}
}, 
{
"funcscope_category": {
"id": 2
}
}, 
{
"funcscope_category": {
"id": 3
}
}
]
}
}

大家开发时要注意json数据结构

三、关于代码

微信公众账号第三方平台全网发布源码(java)- 实战测试通过 代码结构基础上进行修改

 /**
     * 微信全网测试账号
     */
    private final static String COMPONENT_TOKEN = "XXXXX";
    private final static String COMPONENT_ENCODINGAESKEY = "XXXXXX";

    //公众号第三方service,可获取appid,appsecret等信息
    @Autowired
    private IThirdWeixinService thirdWeixinService;

    /**
     * 授权事件接收
     * 
     * @param request
     * @param response
     * @throws IOException
     * @throws AesException
     * @throws DocumentException
     */
    @RequestMapping(value = "/event/authorize")
    public void acceptAuthorizeEvent(HttpServletRequest request,
            HttpServletResponse response) throws IOException, AesException,
            DocumentException {
         System.out.println("微信第三方平台---------微信推送Ticket消息10分钟一次-----------");
        // DataUtils.getDataString(DataUtils.yyyymmddhhmmss));
        output(response, "success"); // 输出响应的内容。
        processAuthorizeEvent(request);

    }

    @RequestMapping(value = "/callback/{appid}")
    public void acceptMessageAndEvent(HttpServletRequest request, @PathVariable("appid") String appid,
            HttpServletResponse response) throws IOException, AesException,
            DocumentException {

        String msgSignature = request.getParameter("msg_signature");  
        System.out.println("第三方平台全网发布-------------{appid}/callback-----------验证开始。。。。msg_signature="+msgSignature);  
        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, APPID)) {  
        System.out.println("全网发布接入检测消息反馈开始---------------APPID="+ appid +"------------------------toUserName="+toUserName);  
           checkWeixinAllNetworkCheck(request,response,xml,appid);  
//        }  
    }

    // 公众号运营者授权后,进入回调页面获得authorization_code,获取公众号信息
    @RequestMapping(value = "/getauthinfo")
    @ResponseBody
    public String getauthinfo(HttpServletRequest request, String authorization_code,String entCode,
            HttpServletResponse response) throws Exception
             {

        String url = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token="+thirdWeixinService.getComponent_access_token();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("component_appid", thirdWeixinService.getThirdWeixinBase().getComponent_appid());
        jsonObject.put("authorization_code", authorization_code);
        JSONObject jsonRes = WeixinToKenServiceImpl.httpRequest(url, "POST", jsonObject); 

        ThirdWeixin thirdWeixin = new ThirdWeixin();
        thirdWeixin = JSON.parseObject(JSON.toJSONString(jsonRes.get("authorization_info")), ThirdWeixin.class);
        thirdWeixin.setEntCode(entCode);
        CommonUtil.setInsertCommonField(thirdWeixin, "system_getauthinfo");
        thirdWeixinService.saveThirdWeixin(thirdWeixin);

        return "success";
    }

    @RequestMapping(value = "/third/getuserinfo")
    @ResponseBody
    public String getthirduserinfo(HttpServletRequest request,String entCode,String code, String authLevel,
            String productId,
            HttpServletResponse response) throws Exception
             {

        Map<String, Object> hash = new HashMap<String, Object>();

        ThirdWeixinBase thirdWeixinBase = thirdWeixinService.getThirdWeixinBase();
        ThirdWeixin thirdWei = new ThirdWeixin();
        thirdWei.setEntCode(entCode);
        ThirdWeixin thirdWeixin = thirdWeixinService.getThirdWeixin(thirdWei);
        String url = "https://api.weixin.qq.com/sns/oauth2/component/access_token?appid="+thirdWeixin.getAuthorizer_appid()+"&code="+code+"&grant_type=authorization_code&component_appid="+thirdWeixinBase.getComponent_appid()+"&component_access_token="+thirdWeixinService.getComponent_access_token();
        JSONObject openidObject = WeixinToKenServiceImpl.httpRequest(url, "GET", null); 

        // 验证该用户是否有写评价资格
        User u = new User();
        u.setOpenid(openidObject.get("openid").toString());
        u.setEntCode(entCode);
        User user = userService.getUser(u);
        if (user != null) {
            Review review = new Review();
            review.setUserId(user.getUserId());
            review.setProductId(productId);
            List<Review> reviewList = reviewService.getReviewByTime(
                    DateTimeUtil.getCurrDate2(), DateTimeUtil.getCurrDate2(),
                    review);

            if (reviewList.size() >= 2) {
                hash.put("msg", "超过评价次数");
                return JSON.toJSONString(hash);
            }
        }
        if ("1".equals(authLevel)) {
            return JSON.toJSONString(openidObject);
        } else {
            JSONObject userInfoObject = WeixinUtil.getUserInfo(openidObject
                    .get("access_token").toString(), openidObject.get("openid")
                    .toString());
            return JSON.toJSONString(userInfoObject);
        }

    }

    public void checkWeixinAllNetworkCheck(HttpServletRequest request, HttpServletResponse response,String xml, String appid) throws DocumentException, IOException, AesException{  
        String nonce = request.getParameter("nonce");  
        String timestamp = request.getParameter("timestamp");  
        String msgSignature = request.getParameter("msg_signature");  

        WXBizMsgCrypt pc = new WXBizMsgCrypt(COMPONENT_TOKEN, COMPONENT_ENCODINGAESKEY, thirdWeixinService.getThirdWeixinBase().getComponent_appid());  
        xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml);  

        Document doc = DocumentHelper.parseText(xml);  
        Element rootElt = doc.getRootElement();  
        String msgType = rootElt.elementText("MsgType");  
        String toUserName = rootElt.elementText("ToUserName");  
        String fromUserName = rootElt.elementText("FromUserName");  

        System.out.println("---全网发布接入检测--step.1-----------msgType="+msgType+"-----------------toUserName="+toUserName+"-----------------fromUserName="+fromUserName);  
//        LogUtil.info("---全网发布接入检测--step.2-----------xml="+xml);  
        if("event".equals(msgType)){  
//           LogUtil.info("---全网发布接入检测--step.3-----------事件消息--------");  
             String event = rootElt.elementText("Event");  
             replyEventMessage(request,response,event,toUserName,fromUserName,appid);  
        }else if("text".equals(msgType)){  
//           LogUtil.info("---全网发布接入检测--step.3-----------文本消息--------");  
             String content = rootElt.elementText("Content");  
             processTextMessage(request,response,content,toUserName,fromUserName,appid);  
        }  
    }  

    public void replyEventMessage(HttpServletRequest request, HttpServletResponse response, String event, String toUserName, String fromUserName, String appid) throws DocumentException, IOException {  
        String content = event + "from_callback";  
//        LogUtil.info("---全网发布接入检测------step.4-------事件回复消息  content="+content + "   toUserName="+toUserName+"   fromUserName="+fromUserName);  
        replyTextMessage(request,response,content,toUserName,fromUserName, appid);  
    }  

    public void processTextMessage(HttpServletRequest request, HttpServletResponse response,String content,String toUserName, String fromUserName, String appid) throws IOException, DocumentException{  
        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);  
        }  
    }  

    public void replyApiTextMessage(HttpServletRequest request, HttpServletResponse response, String auth_code, String fromUserName, String appid) throws DocumentException, IOException {  
        // 得到微信授权成功的消息后,应该立刻进行处理!!相关信息只会在首次授权的时候推送过来  
        System.out.println("------step.1----使用客服消息接口回复粉丝----逻辑开始-------------------------");  
        try {  
            System.out.println("------step.1----使用客服消息接口回复粉丝----逻辑开始-------------------------auth_code: "+auth_code+"  thirdWeixinService.getComponent_access_token:"+thirdWeixinService.getComponent_access_token());
            String url = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token="+thirdWeixinService.getComponent_access_token();
            JSONObject jsonObject1 = new JSONObject();
            jsonObject1.put("component_appid", thirdWeixinService.getThirdWeixinBase().getComponent_appid());
            jsonObject1.put("authorization_code", auth_code);
            JSONObject jsonRes = WeixinToKenServiceImpl.httpRequest(url, "POST", jsonObject1); 
            System.out.println("------step.1----使用客服消息接口回复粉丝----逻辑开始---------------------jsonRes:"+jsonRes.toString());  

            System.out.println("------step.1----使用客服消息接口回复粉丝----逻辑开始---------------------jsonRes.authorization_info:"+jsonRes.get("authorization_info"));  
            ThirdWeixin thirdWeixin = new ThirdWeixin();
            thirdWeixin = JSON.parseObject(JSON.toJSONString(jsonRes.get("authorization_info")), ThirdWeixin.class);
            thirdWeixin.setEntCode("test");
            CommonUtil.setInsertCommonField(thirdWeixin, "system_getauthinfo");
            thirdWeixinService.saveThirdWeixin(thirdWeixin);


            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);
            WeixinToKenServiceImpl.httpRequest("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="+thirdWeixin.getAuthorizer_access_token(), "POST", jsonObject); 
        } catch (Exception e) {  
            e.printStackTrace();  
        }  

    }  

    /** 
     * 回复微信服务器"文本消息" 
     * @param request 
     * @param response 
     * @param content 
     * @param toUserName 
     * @param fromUserName 
     * @throws DocumentException 
     * @throws IOException 
     */  
    public void replyTextMessage(HttpServletRequest request, HttpServletResponse response, String content, String toUserName, String fromUserName, String appid) 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(COMPONENT_TOKEN, COMPONENT_ENCODINGAESKEY, thirdWeixinService.getThirdWeixinBase().getComponent_appid());  
            returnvaleue = pc.encryptMsg(replyMsg, createTime.toString(), "easemob");  
//            System.out.println("------------------加密后的返回内容 returnvaleue: "+returnvaleue);  
        } catch (AesException e) {  
            e.printStackTrace();  
        }  
        output(response, returnvaleue);  
    }  


    /**
     * 处理授权事件的推送
     * 
     * @param request
     * @throws IOException
     * @throws AesException
     * @throws DocumentException
     */
    public void processAuthorizeEvent(HttpServletRequest request)
            throws IOException, DocumentException, AesException {
        String nonce = request.getParameter("nonce");
        String timestamp = request.getParameter("timestamp");
        String msgSignature = request.getParameter("msg_signature");
        System.out.print(nonce+"  "+timestamp+"  "+msgSignature);
        StringBuilder sb = new StringBuilder();
        BufferedReader in = request.getReader();
        String line;
        while ((line = in.readLine()) != null) {
            sb.append(line);
        }
        String xml = sb.toString();
        System.out.println("第三方平台全网发布-----------------------原始 Xml="+xml);
        String encodingAesKey = COMPONENT_ENCODINGAESKEY;// 第三方平台组件加密密钥
        // LogUtil.info("第三方平台全网发布-------------appid----------getAuthorizerAppidFromXml(xml)-----------appId="+appId);
        System.out.println("第三方平台全网发布-----------------------getThirdWeixinBase.toString():"+thirdWeixinService.getThirdWeixinBase().toString());
        WXBizMsgCrypt pc = new WXBizMsgCrypt(COMPONENT_TOKEN, encodingAesKey,
                thirdWeixinService.getThirdWeixinBase().getComponent_appid());
        System.out.println("第三方平台全网发布-----------------------解密 WXBizMsgCrypt new 成功");
        String xml1 = pc.decryptMsg(msgSignature, timestamp, nonce, xml);
        System.out.println("第三方平台全网发布-----------------------解密后 Xml="+xml1);
        processAuthorizationEvent(xml1);
    }

    public static void main(String args[]) throws IOException {

        String xml = null;
        try {
            WXBizMsgCrypt pc = new WXBizMsgCrypt(COMPONENT_TOKEN, COMPONENT_ENCODINGAESKEY,
                    "wx4addc310e841f58b");
            xml = pc.decryptMsg("cbff9928c63f34e6fe0f3d63bf7ad8c5ab2322a3", "1481013633", "1836283428", "<xml>    <AppId><![CDATA[wx4addc310e841f58b]]></AppId>    <Encrypt><![CDATA[ouuB6+xRWHaoVpPqvgLJXztr3vnxrF9rWR0K8X7nU3DfjJBgc5NtxJM7C00Q8Ogx7vJSKkiv4T54EPIPn1cDORqZ/x0MVedRAKrRYRX7f5Cju+nBCF7+5YoXLbuT2HigTA1NrGz3aiYTTnwehlrquO9LwwggriDuvhETseHaa2lCoN9A18hFbMPcTIZobeNEVh97omziTBbq6bOOpWl1QSmjk89GnmhUN7f2MXqY+SQiLg1xdk6Tk6jh2MtCAmJdLQOKq+FFVLogarqBwn2G03y4/zzKwriDjYR80Yo4iHnz+7Vewlp6n0yw8BNXE5smnRe5FFzO4IMAODxKe8pPahKTOYrjqOez+AmER+7EW6N/6OGWsTqNfTmFSl1z/W3amXhBIXQXu+CJy/7lcwEYkGvaFA2PVSf1nwfqaWFxP72gpYHUemQe00aMkbtFi8wvNw8k6cDNuZ0CTp7zJLV/Lg==]]></Encrypt></xml>");
        } catch (AesException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("第三方平台全网发布-----------------------解密后 Xml="+xml);

    }
    /**
     * 保存Ticket
     * 
     * @param xml
     */
    void processAuthorizationEvent(String xml) {
        Document doc;
        try {
            doc = DocumentHelper.parseText(xml);
            Element rootElt = doc.getRootElement();
            String ticket = rootElt.elementText("ComponentVerifyTicket");
            System.out.println("第三方平台全网发布-----------------------解密后 ComponentVerifyTicket="+ticket);
            if(ticket != null){
                thirdWeixinService.setComponent_verify_ticket(ticket);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取授权的Appid
     * 
     * @param xml
     * @return
     */
    String getAuthorizerAppidFromXml(String xml) {
        Document doc;
        try {
            doc = DocumentHelper.parseText(xml);
            Element rootElt = doc.getRootElement();
            String toUserName = rootElt.elementText("ToUserName");
            return toUserName;
        } catch (DocumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 
     * @param response
     * @param returnvaleue
     */
    public void output(HttpServletResponse response, String returnvaleue) {
        try {
            PrintWriter pw = response.getWriter();
            pw.write(returnvaleue);
            // System.out.println("****************returnvaleue***************="+returnvaleue);
            pw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
  • 10
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值