微信开发(4):微信第三方开放平台的搭建(java)

什么是第三方开放平台

来波官方解释:
我才是官方文档

第三方平台的开放,让公众号或小程序运营者在面向垂直行业需求时,可以通过一键登录授权给第三方开发者,来完成相关能力。

简单的说,就是让公众号授权给第三个开放平台,根据授权不同,第三开放平台可以获取到该公众号的接口权限,从而直接调用微信api,进行公众号开发;

开通创建流程
这里写图片描述

开发者资质审核通过后,就可以创建第三方开放平台了,由于我们第三方开放平台已经建立了。就不演示创建平台的过程了,下面解释下填写的资料

  1. 第一步基本资料填写,不做多余说明,填写基本资料即可
  2. 第二步选择权限集,大概意思就是 选择下 你这个第三方开放平台 要代替公众号实现那些业务 获取公众号的那些接口权限,需要注意的是首先要确保该公众号已经有了这个权限 我是官方文档
  3. 第三步是填写开发资料,基本上就是域名,白名单一些的配置了 这里官方文档写的也比较清楚 不懂看这里

开始开发
首先要做的 是授权事件接收URL的处理,用于接收取消授权通知、授权成功通知、授权更新通知,也用于接收ticket,ticket是验证平台方的重要凭据,服务方在获取component_access_token时需要提供最新推送的ticket以供验证身份合法性。此ticket作为验证服务方的重要凭据,请妥善保存。

权限变更这些可以先不考虑,先考虑的是 接受这个ticket,只有拥有了ticket才能去换取第三方平台的token
下面是主要代码。实体类这些不提供了,根据需求可以自行创建

 package com.ysh.wxtest.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.websocket.Session;

import org.apache.commons.lang.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import com.ysh.wxtest.model.Users;
import com.ysh.wxtest.model.Wxauthinfo;
import com.ysh.wxtest.model.Wxauthorizerinfo;
import com.ysh.wxtest.model.Wxcomtoken;
import com.ysh.wxtest.model.Wxticket;
import com.ysh.wxtest.service.WxauthinfoService;
import com.ysh.wxtest.service.WxauthorizerinfoService;
import com.ysh.wxtest.service.WxcomtokenService;
import com.ysh.wxtest.service.WxticketService;
import com.ysh.wxtest.util.AjaxMessage;

import common.Logger;
import weixin.popular.bean.component.*;
import weixin.popular.util.XMLConverUtil;


/**
 * 微信 第三方开放平台  controller层
 * @author YaoShiHang
 *
 */
@Controller
public class WeixinAccreditController {

    @Autowired
    private WxticketService  wxticketService;   //微信推送 ticket服务,用于保存 ticket
    @Autowired
    private WxcomtokenService wxcomtokenService; //微信第三方平台  token服务 ,有效期2小时。获取次数有限 注意缓存
    @Autowired
    private WxauthinfoService wxauthinfoService; //微信授权信息 服务   
    @Autowired
    private WxauthorizerinfoService WxinfoService; 

    private final String APPID = "???";

    private Logger log= Logger.getLogger(getClass());

    /**
     * 微信全网测试账号
     */
    private final static String COMPONENT_APPID = "XXXXXXXXXXXX"; //第三方平台 APPID
    private final String COMPONENT_APPSECRET = "XXXXXXXXXXXX";    //第三方平台 秘钥
    private final static String COMPONENT_ENCODINGAESKEY = "XXXXXXXXXXXX";  //开发者 设置的 key
    private final static String COMPONENT_TOKEN = "XXXXXXXXXXXX";   //开发者 设置的 token 

    // 授权事件接受url 每隔10分钟 获取微信服务器推送ticket 接收后需要解密 接收到后 必须返回字符串success
    @RequestMapping("/openwx/getticket")
    public void getTicket(HttpServletRequest request, HttpServletResponse response)
            throws IOException, DocumentException, AesException {
        processAuthorizeEvent(request);
        output(response, "success"); // 输出响应的内容。
    }

    /**
     *  授权事件处理 
     * @param request
     * @throws IOException
     * @throws DocumentException
     * @throws AesException
     */
    public void processAuthorizeEvent(HttpServletRequest request) throws IOException, DocumentException, AesException {
        String nonce = request.getParameter("nonce");
        String timestamp = request.getParameter("timestamp");
        String signature = request.getParameter("signature");
        String msgSignature = request.getParameter("msg_signature");
        HttpSession session  = request.getSession();
        if (!StringUtils.isNotBlank(msgSignature)){ //判断消息是否空
            return;// 微信推送给第三方开放平台的消息一定是加过密的,无消息加密无法解密消息
        }
        boolean isValid = checkSignature(COMPONENT_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();
              log.info("第三方平台全网发布-----------------------原始 Xml="+xml);
            String encodingAesKey = COMPONENT_ENCODINGAESKEY;// 第三方平台组件加密密钥
            String appId =  (xml);// 此时加密的xml数据中ToUserName是非加密的,解析xml获取即可
               log.info("第三方平台全网发布-------------appid----------getAuthorizerAppidFromXml(xml)-----------appId="+appId);
            WXBizMsgCrypt pc = new WXBizMsgCrypt(COMPONENT_TOKEN, encodingAesKey, COMPONENT_APPID);
            xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml);
               log.info("第三方平台全网发布-----------------------解密后 Xml="+xml);
            ComponentReceiveXML com = XMLConverUtil.convertToObject(ComponentReceiveXML.class, xml);
            session.setAttribute("com",com);
            processAuthorizationEvent(xml);
        }
    }
    /**
     * 保存Ticket
     * @param xml
     */
    void processAuthorizationEvent(String xml) {
        Document doc;

        try {
            doc = DocumentHelper.parseText(xml);
            Element rootElt = doc.getRootElement();
            String ticket = rootElt.elementText("ComponentVerifyTicket");
            if(ticket!=null){
                Wxticket wxticket = new Wxticket();
                wxticket.setAppid(APPID);
                wxticket.setAddtime(new Date());;
                wxticket.setId(1l);
                wxticket.setTicket(ticket);
                wxticketService.updateNotNull(wxticket);
            }
        } catch (DocumentException e) {
            e.printStackTrace();
        }
    }

    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 token
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {
        System.out.println(
                "###token:" + token + ";signature:" + signature + ";timestamp:" + timestamp + "nonce:" + nonce);
        boolean flag = false;
        if (signature != null && !signature.equals("") && timestamp != null && !timestamp.equals("") && nonce != null
                && !nonce.equals("")) {
            String sha1 = "";
            String[] ss = new String[] { token, timestamp, nonce };
            Arrays.sort(ss);
            for (String s : ss) {
                sha1 += s;
            }
            sha1 = AddSHA1.SHA1(sha1);
            if (sha1.equals(signature)) {
                flag = true;
            }
        }
        return flag;
    }
    /**
     * 工具类:回复微信服务器"文本消息"
     * @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();
        }
    }

}

class AddSHA1 {
    public static String SHA1(String inStr) {
        MessageDigest md = null;
        String outStr = null;
        try {
            md = MessageDigest.getInstance("SHA-1"); // 选择SHA-1,也可以选择MD5
            byte[] digest = md.digest(inStr.getBytes()); // 返回的是byet[],要转化为String存储比较方便
            outStr = bytetoString(digest);
        } catch (NoSuchAlgorithmException nsae) {
            nsae.printStackTrace();
        }
        return outStr;
    }
    public static String bytetoString(byte[] digest) {
        String str = "";
        String tempStr = "";
        for (int i = 0; i < digest.length; i++) {
            tempStr = (Integer.toHexString(digest[i] & 0xff));
            if (tempStr.length() == 1) {
                str = str + "0" + tempStr;
            } else {
                str = str + tempStr;
            }
        }
        return str.toLowerCase();
    }
}

提供一个xml 解析工具类


import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.DOMException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.sun.xml.bind.marshaller.CharacterEscapeHandler;

/**
 * XML 数据接收对象转换工具类
 * @author LiYi
 *
 */
public class XMLConverUtil{

    private static final ThreadLocal<Map<Class<?>,Marshaller>> mMapLocal = new ThreadLocal<Map<Class<?>,Marshaller>>() {
        @Override
        protected Map<Class<?>, Marshaller> initialValue() {
            return new HashMap<Class<?>, Marshaller>();
        }
    };

    private static final ThreadLocal<Map<Class<?>,Unmarshaller>> uMapLocal = new ThreadLocal<Map<Class<?>,Unmarshaller>>(){
        @Override
        protected Map<Class<?>, Unmarshaller> initialValue() {
            return new HashMap<Class<?>, Unmarshaller>();
        }
    };

    /**
     * XML to Object
     * @param <T> T
     * @param clazz clazz
     * @param xml xml
     * @return T
     */
    public static <T> T convertToObject(Class<T> clazz,String xml){
        return convertToObject(clazz,new StringReader(xml));
    }

    /**
     * XML to Object
     * @param <T> T
     * @param clazz clazz
     * @param inputStream  inputStream
     * @return T
     */
    public static <T> T convertToObject(Class<T> clazz,InputStream inputStream){
        return convertToObject(clazz,new InputStreamReader(inputStream));
    }

    /**
     * XML to Object
     * @param <T> T
     * @param clazz clazz
     * @param reader reader
     * @return T
     */
    @SuppressWarnings("unchecked")
    public static <T> T convertToObject(Class<T> clazz,Reader reader){
        try {
            Map<Class<?>, Unmarshaller> uMap = uMapLocal.get();
            if(!uMap.containsKey(clazz)){
                JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
                Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
                uMap.put(clazz, unmarshaller);
            }
            return (T) uMap.get(clazz).unmarshal(reader);
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * Object to XML
     * @param object object
     * @return xml
     */
    public static String convertToXML(Object object){
        try {
            Map<Class<?>, Marshaller> mMap = mMapLocal.get();
            if(!mMap.containsKey(object.getClass())){
                JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass());
                Marshaller marshaller = jaxbContext.createMarshaller();
                marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
                //设置CDATA输出字符
                marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() {
                    public void escape(char[] ac, int i, int j, boolean flag, Writer writer) throws IOException {
                        writer.write(ac, i, j);
                    }
                });
                mMap.put(object.getClass(), marshaller);
            }
            StringWriter stringWriter = new StringWriter();
            mMap.get(object.getClass()).marshal(object,stringWriter);
            return stringWriter.getBuffer().toString();
        } catch (JAXBException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 转换简单的xml to map
     * @param xml xml
     * @return map
     */
    public static Map<String,String> convertToMap(String xml){
        Map<String, String> map = new LinkedHashMap<String,String>();
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xml);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);

            Element root = document.getDocumentElement();
            if(root != null){
                NodeList childNodes = root.getChildNodes();
                if(childNodes != null && childNodes.getLength()>0){
                    for(int i = 0;i < childNodes.getLength();i++){
                        Node node = childNodes.item(i); 
                        if( node != null && node.getNodeType() == Node.ELEMENT_NODE){
                            map.put(node.getNodeName(), node.getTextContent());
                        }
                    }
                }
            }
        } catch (DOMException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return map;
    }
}

这一步结束后 第三方开放平台就可以审核通过了。然后就是进行代公众号实现业务了,相对来说简单了很多,微信提供的都有相关文档。直接调用接口就行了。需要注意的是在首先要确保公众号获取了这个权限,其次确保公众号把这个权限授权给了第三方平台。在开发中代公众号实现网页授权的接口 假如使用第三方开放平台的话,接口地址是有所改变的,参考第三方开放平台开发文档,其他的接口 只需将原来公众号的token 改变为 通过第三方开放平台token 获取的公众号授权给第三方开放平台的token 两个token是不一样的。有效期都是2小时 需要缓存。最后上一波成功图片

这里写图片描述

这里写图片描述

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值