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


    什么是第三方开放平台

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

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

    开通创建流程 

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

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

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

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

      1  package com.ysh.wxtest.controller;
      2 
      3 import java.io.BufferedReader;
      4 import java.io.IOException;
      5 import java.io.PrintWriter;
      6 import java.security.MessageDigest;
      7 import java.security.NoSuchAlgorithmException;
      8 import java.util.Arrays;
      9 import java.util.Date;
     10 import java.util.List;
     11 
     12 import javax.servlet.http.HttpServletRequest;
     13 import javax.servlet.http.HttpServletResponse;
     14 import javax.servlet.http.HttpSession;
     15 import javax.websocket.Session;
     16 
     17 import org.apache.commons.lang.StringUtils;
     18 import org.dom4j.Document;
     19 import org.dom4j.DocumentException;
     20 import org.dom4j.DocumentHelper;
     21 import org.dom4j.Element;
     22 import org.springframework.beans.factory.annotation.Autowired;
     23 import org.springframework.stereotype.Controller;
     24 import org.springframework.web.bind.annotation.RequestMapping;
     25 import org.springframework.web.bind.annotation.ResponseBody;
     26 import org.springframework.web.servlet.ModelAndView;
     27 
     28 import com.qq.weixin.mp.aes.AesException;
     29 import com.qq.weixin.mp.aes.WXBizMsgCrypt;
     30 import com.ysh.wxtest.model.Users;
     31 import com.ysh.wxtest.model.Wxauthinfo;
     32 import com.ysh.wxtest.model.Wxauthorizerinfo;
     33 import com.ysh.wxtest.model.Wxcomtoken;
     34 import com.ysh.wxtest.model.Wxticket;
     35 import com.ysh.wxtest.service.WxauthinfoService;
     36 import com.ysh.wxtest.service.WxauthorizerinfoService;
     37 import com.ysh.wxtest.service.WxcomtokenService;
     38 import com.ysh.wxtest.service.WxticketService;
     39 import com.ysh.wxtest.util.AjaxMessage;
     40 
     41 import common.Logger;
     42 import weixin.popular.bean.component.*;
     43 import weixin.popular.util.XMLConverUtil;
     44 
     45 
     46 /**
     47  * 微信 第三方开放平台  controller层
     48  * @author YaoShiHang
     49  *
     50  */
     51 @Controller
     52 public class WeixinAccreditController {
     53 
     54     @Autowired
     55     private WxticketService  wxticketService;   //微信推送 ticket服务,用于保存 ticket
     56     @Autowired
     57     private WxcomtokenService wxcomtokenService; //微信第三方平台  token服务 ,有效期2小时。获取次数有限 注意缓存
     58     @Autowired
     59     private WxauthinfoService wxauthinfoService; //微信授权信息 服务   
     60     @Autowired
     61     private WxauthorizerinfoService WxinfoService; 
     62 
     63     private final String APPID = "???";
     64 
     65     private Logger log= Logger.getLogger(getClass());
     66 
     67     /**
     68      * 微信全网测试账号
     69      */
     70     private final static String COMPONENT_APPID = "XXXXXXXXXXXX"; //第三方平台 APPID
     71     private final String COMPONENT_APPSECRET = "XXXXXXXXXXXX";    //第三方平台 秘钥
     72     private final static String COMPONENT_ENCODINGAESKEY = "XXXXXXXXXXXX";  //开发者 设置的 key
     73     private final static String COMPONENT_TOKEN = "XXXXXXXXXXXX";   //开发者 设置的 token 
     74 
     75     // 授权事件接受url 每隔10分钟 获取微信服务器推送ticket 接收后需要解密 接收到后 必须返回字符串success
     76     @RequestMapping("/openwx/getticket")
     77     public void getTicket(HttpServletRequest request, HttpServletResponse response)
     78             throws IOException, DocumentException, AesException {
     79         processAuthorizeEvent(request);
     80         output(response, "success"); // 输出响应的内容。
     81     }
     82 
     83     /**
     84      *  授权事件处理 
     85      * @param request
     86      * @throws IOException
     87      * @throws DocumentException
     88      * @throws AesException
     89      */
     90     public void processAuthorizeEvent(HttpServletRequest request) throws IOException, DocumentException, AesException {
     91         String nonce = request.getParameter("nonce");
     92         String timestamp = request.getParameter("timestamp");
     93         String signature = request.getParameter("signature");
     94         String msgSignature = request.getParameter("msg_signature");
     95         HttpSession session  = request.getSession();
     96         if (!StringUtils.isNotBlank(msgSignature)){ //判断消息是否空
     97             return;// 微信推送给第三方开放平台的消息一定是加过密的,无消息加密无法解密消息
     98         }
     99         boolean isValid = checkSignature(COMPONENT_TOKEN, signature, timestamp, nonce);
    100         if (isValid) {
    101             StringBuilder sb = new StringBuilder();
    102             BufferedReader in = request.getReader();
    103             String line;
    104             while ((line = in.readLine()) != null) {
    105                 sb.append(line);
    106             }
    107             String xml = sb.toString();
    108               log.info("第三方平台全网发布-----------------------原始 Xml="+xml);
    109             String encodingAesKey = COMPONENT_ENCODINGAESKEY;// 第三方平台组件加密密钥
    110             String appId =  (xml);// 此时加密的xml数据中ToUserName是非加密的,解析xml获取即可
    111                log.info("第三方平台全网发布-------------appid----------getAuthorizerAppidFromXml(xml)-----------appId="+appId);
    112             WXBizMsgCrypt pc = new WXBizMsgCrypt(COMPONENT_TOKEN, encodingAesKey, COMPONENT_APPID);
    113             xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml);
    114                log.info("第三方平台全网发布-----------------------解密后 Xml="+xml);
    115             ComponentReceiveXML com = XMLConverUtil.convertToObject(ComponentReceiveXML.class, xml);
    116             session.setAttribute("com",com);
    117             processAuthorizationEvent(xml);
    118         }
    119     }
    120     /**
    121      * 保存Ticket
    122      * @param xml
    123      */
    124     void processAuthorizationEvent(String xml) {
    125         Document doc;
    126 
    127         try {
    128             doc = DocumentHelper.parseText(xml);
    129             Element rootElt = doc.getRootElement();
    130             String ticket = rootElt.elementText("ComponentVerifyTicket");
    131             if(ticket!=null){
    132                 Wxticket wxticket = new Wxticket();
    133                 wxticket.setAppid(APPID);
    134                 wxticket.setAddtime(new Date());;
    135                 wxticket.setId(1l);
    136                 wxticket.setTicket(ticket);
    137                 wxticketService.updateNotNull(wxticket);
    138             }
    139         } catch (DocumentException e) {
    140             e.printStackTrace();
    141         }
    142     }
    143 
    144     String getAuthorizerAppidFromXml(String xml) {
    145         Document doc;
    146         try {
    147             doc = DocumentHelper.parseText(xml);
    148             Element rootElt = doc.getRootElement();
    149             String toUserName = rootElt.elementText("ToUserName");
    150             return toUserName;
    151         } catch (DocumentException e) {
    152             // TODO Auto-generated catch block
    153             e.printStackTrace();
    154         }
    155         return null;
    156     }
    157 
    158     /**
    159      * 判断消息是否加密
    160      * @param token
    161      * @param signature
    162      * @param timestamp
    163      * @param nonce
    164      * @return
    165      */
    166     public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {
    167         System.out.println(
    168                 "###token:" + token + ";signature:" + signature + ";timestamp:" + timestamp + "nonce:" + nonce);
    169         boolean flag = false;
    170         if (signature != null && !signature.equals("") && timestamp != null && !timestamp.equals("") && nonce != null
    171                 && !nonce.equals("")) {
    172             String sha1 = "";
    173             String[] ss = new String[] { token, timestamp, nonce };
    174             Arrays.sort(ss);
    175             for (String s : ss) {
    176                 sha1 += s;
    177             }
    178             sha1 = AddSHA1.SHA1(sha1);
    179             if (sha1.equals(signature)) {
    180                 flag = true;
    181             }
    182         }
    183         return flag;
    184     }
    185     /**
    186      * 工具类:回复微信服务器"文本消息"
    187      * @param response
    188      * @param returnvaleue
    189      */
    190     public void output(HttpServletResponse response, String returnvaleue) {
    191         try {
    192             PrintWriter pw = response.getWriter();
    193             pw.write(returnvaleue);
    194             System.out.println("****************returnvaleue***************="+returnvaleue);
    195             pw.flush();
    196         } catch (IOException e) {
    197             e.printStackTrace();
    198         }
    199     }
    200 
    201 }
    202 
    203 class AddSHA1 {
    204     public static String SHA1(String inStr) {
    205         MessageDigest md = null;
    206         String outStr = null;
    207         try {
    208             md = MessageDigest.getInstance("SHA-1"); // 选择SHA-1,也可以选择MD5
    209             byte[] digest = md.digest(inStr.getBytes()); // 返回的是byet[],要转化为String存储比较方便
    210             outStr = bytetoString(digest);
    211         } catch (NoSuchAlgorithmException nsae) {
    212             nsae.printStackTrace();
    213         }
    214         return outStr;
    215     }
    216     public static String bytetoString(byte[] digest) {
    217         String str = "";
    218         String tempStr = "";
    219         for (int i = 0; i < digest.length; i++) {
    220             tempStr = (Integer.toHexString(digest[i] & 0xff));
    221             if (tempStr.length() == 1) {
    222                 str = str + "0" + tempStr;
    223             } else {
    224                 str = str + tempStr;
    225             }
    226         }
    227         return str.toLowerCase();
    228     }
    229 }

    提供一个xml 解析工具类

      1 import java.io.IOException;
      2 import java.io.InputStream;
      3 import java.io.InputStreamReader;
      4 import java.io.Reader;
      5 import java.io.StringReader;
      6 import java.io.StringWriter;
      7 import java.io.Writer;
      8 import java.util.HashMap;
      9 import java.util.LinkedHashMap;
     10 import java.util.Map;
     11 
     12 import javax.xml.bind.JAXBContext;
     13 import javax.xml.bind.JAXBException;
     14 import javax.xml.bind.Marshaller;
     15 import javax.xml.bind.Unmarshaller;
     16 import javax.xml.parsers.DocumentBuilder;
     17 import javax.xml.parsers.DocumentBuilderFactory;
     18 import javax.xml.parsers.ParserConfigurationException;
     19 
     20 import org.w3c.dom.DOMException;
     21 import org.w3c.dom.Document;
     22 import org.w3c.dom.Element;
     23 import org.w3c.dom.Node;
     24 import org.w3c.dom.NodeList;
     25 import org.xml.sax.InputSource;
     26 import org.xml.sax.SAXException;
     27 
     28 import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
     29 
     30 /**
     31  * XML 数据接收对象转换工具类
     32  * @author LiYi
     33  *
     34  */
     35 public class XMLConverUtil{
     36 
     37     private static final ThreadLocal<Map<Class<?>,Marshaller>> mMapLocal = new ThreadLocal<Map<Class<?>,Marshaller>>() {
     38         @Override
     39         protected Map<Class<?>, Marshaller> initialValue() {
     40             return new HashMap<Class<?>, Marshaller>();
     41         }
     42     };
     43 
     44     private static final ThreadLocal<Map<Class<?>,Unmarshaller>> uMapLocal = new ThreadLocal<Map<Class<?>,Unmarshaller>>(){
     45         @Override
     46         protected Map<Class<?>, Unmarshaller> initialValue() {
     47             return new HashMap<Class<?>, Unmarshaller>();
     48         }
     49     };
     50 
     51     /**
     52      * XML to Object
     53      * @param <T> T
     54      * @param clazz clazz
     55      * @param xml xml
     56      * @return T
     57      */
     58     public static <T> T convertToObject(Class<T> clazz,String xml){
     59         return convertToObject(clazz,new StringReader(xml));
     60     }
     61 
     62     /**
     63      * XML to Object
     64      * @param <T> T
     65      * @param clazz clazz
     66      * @param inputStream  inputStream
     67      * @return T
     68      */
     69     public static <T> T convertToObject(Class<T> clazz,InputStream inputStream){
     70         return convertToObject(clazz,new InputStreamReader(inputStream));
     71     }
     72 
     73     /**
     74      * XML to Object
     75      * @param <T> T
     76      * @param clazz clazz
     77      * @param reader reader
     78      * @return T
     79      */
     80     @SuppressWarnings("unchecked")
     81     public static <T> T convertToObject(Class<T> clazz,Reader reader){
     82         try {
     83             Map<Class<?>, Unmarshaller> uMap = uMapLocal.get();
     84             if(!uMap.containsKey(clazz)){
     85                 JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
     86                 Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
     87                 uMap.put(clazz, unmarshaller);
     88             }
     89             return (T) uMap.get(clazz).unmarshal(reader);
     90         } catch (JAXBException e) {
     91             e.printStackTrace();
     92         }
     93         return null;
     94     }
     95 
     96     /**
     97      * Object to XML
     98      * @param object object
     99      * @return xml
    100      */
    101     public static String convertToXML(Object object){
    102         try {
    103             Map<Class<?>, Marshaller> mMap = mMapLocal.get();
    104             if(!mMap.containsKey(object.getClass())){
    105                 JAXBContext jaxbContext = JAXBContext.newInstance(object.getClass());
    106                 Marshaller marshaller = jaxbContext.createMarshaller();
    107                 marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    108                 //设置CDATA输出字符
    109                 marshaller.setProperty(CharacterEscapeHandler.class.getName(), new CharacterEscapeHandler() {
    110                     public void escape(char[] ac, int i, int j, boolean flag, Writer writer) throws IOException {
    111                         writer.write(ac, i, j);
    112                     }
    113                 });
    114                 mMap.put(object.getClass(), marshaller);
    115             }
    116             StringWriter stringWriter = new StringWriter();
    117             mMap.get(object.getClass()).marshal(object,stringWriter);
    118             return stringWriter.getBuffer().toString();
    119         } catch (JAXBException e) {
    120             e.printStackTrace();
    121         }
    122         return null;
    123     }
    124 
    125     /**
    126      * 转换简单的xml to map
    127      * @param xml xml
    128      * @return map
    129      */
    130     public static Map<String,String> convertToMap(String xml){
    131         Map<String, String> map = new LinkedHashMap<String,String>();
    132         try {
    133             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    134             DocumentBuilder db = dbf.newDocumentBuilder();
    135             StringReader sr = new StringReader(xml);
    136             InputSource is = new InputSource(sr);
    137             Document document = db.parse(is);
    138 
    139             Element root = document.getDocumentElement();
    140             if(root != null){
    141                 NodeList childNodes = root.getChildNodes();
    142                 if(childNodes != null && childNodes.getLength()>0){
    143                     for(int i = 0;i < childNodes.getLength();i++){
    144                         Node node = childNodes.item(i); 
    145                         if( node != null && node.getNodeType() == Node.ELEMENT_NODE){
    146                             map.put(node.getNodeName(), node.getTextContent());
    147                         }
    148                     }
    149                 }
    150             }
    151         } catch (DOMException e) {
    152             e.printStackTrace();
    153         } catch (ParserConfigurationException e) {
    154             e.printStackTrace();
    155         } catch (SAXException e) {
    156             e.printStackTrace();
    157         } catch (IOException e) {
    158             e.printStackTrace();
    159         }
    160         return map;
    161     }
    162 }

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

     

  • 相关阅读:
    quratz数据存储
    quratz线程
    scheduler 基本原理
    一致性哈希算法
    高并发流量控制
    049 DSL语句
    048 SparkSQL自定义UDAF函数
    047 SparkSQL自定义UDF函数
    Quratz的理解
    046 SparlSQL中的函数
  • 原文地址:https://www.cnblogs.com/haw2106/p/10345234.html
Copyright © 2020-2023  润新知