微信开发(4):微信第三方开放平台的搭建(java)
2018-03-03 14:25
411 查看
什么是第三方开放平台
来波官方解释:
我才是官方文档
第三方平台的开放,让公众号或小程序运营者在面向垂直行业需求时,可以通过一键登录授权给第三方开发者,来完成相关能力。
简单的说,就是让公众号授权给第三个开放平台,根据授权不同,第三开放平台可以获取到该公众号的接口权限,从而直接调用微信api,进行公众号开发;
开通创建流程
开发者资质审核通过后,就可以创建第三方开放平台了,由于我们第三方开放平台已经建立了。就不演示创建平台的过程了,下面解释下填写的资料
第一步基本资料填写,不做多余说明,填写基本资料即可
第二步选择权限集,大概意思就是 选择下 你这个第三方开放平台 要代替公众号实现那些业务 获取公众号的那些接口权限,需要注意的是首先要确保该公众号已经有了这个权限 我是官方文档
第三步是填写开发资料,基本上就是域名,白名单一些的配置了 这里官方文档写的也比较清楚 不懂看这里
开始开发
首先要做的 是授权事件接收URL的处理,用于接收取消授权通知、授权成功通知、授权更新通知,也用于接收ticket,ticket是验证平台方的重要凭据,服务方在获取component_access_token时需要提供最新推送的ticket以供验证身份合法性。此ticket作为验证服务方的重要凭据,请妥善保存。
权限变更这些可以先不考虑,先考虑的是 接受这个ticket,只有拥有了ticket才能去换取第三方平台的token
下面是主要代码。实体类这些不提供了,根据需求可以自行创建
提供一个xml 解析工具类
这一步结束后 第三方开放平台就可以审核通过了。然后就是进行代公众号实现业务了,相对来说简单了很多,微信提供的都有相关文档。直接调用接口就行了。需要注意的是在首先要确保公众号获取了这个权限,其次确保公众号把这个权限授权给了第三方平台。在开发中代公众号实现网页授权的接口 假如使用第三方开放平台的话,接口地址是有所改变的,参考第三方开放平台开发文档,其他的接口 只需将原来公众号的token 改变为 通过第三方开放平台token 获取的公众号授权给第三方开放平台的token 两个token是不一样的。有效期都是2小时 需要缓存。最后上一波成功图片
来波官方解释:
我才是官方文档
第三方平台的开放,让公众号或小程序运营者在面向垂直行业需求时,可以通过一键登录授权给第三方开发者,来完成相关能力。
简单的说,就是让公众号授权给第三个开放平台,根据授权不同,第三开放平台可以获取到该公众号的接口权限,从而直接调用微信api,进行公众号开发;
开通创建流程
开发者资质审核通过后,就可以创建第三方开放平台了,由于我们第三方开放平台已经建立了。就不演示创建平台的过程了,下面解释下填写的资料
第一步基本资料填写,不做多余说明,填写基本资料即可
第二步选择权限集,大概意思就是 选择下 你这个第三方开放平台 要代替公众号实现那些业务 获取公众号的那些接口权限,需要注意的是首先要确保该公众号已经有了这个权限 我是官方文档
第三步是填写开发资料,基本上就是域名,白名单一些的配置了 这里官方文档写的也比较清楚 不懂看这里
开始开发
首先要做的 是授权事件接收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 = f076 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小时 需要缓存。最后上一波成功图片
相关文章推荐
- 微信公众开放平台开发03---百度BAE上搭建属于自己的微信公众平台 -JAVA,微信公众开放平台部署到百度云中BASE2.0,进行调试,木有钱买云服务器的亲们试试
- 微信开放平台 公众号第三方平台开发 教程二 创建公众号第三方平台
- 微信开放平台之公众号第三方平台开发及全网发布验证
- 微信开放平台 公众号第三方平台开发 教程三 一键登录授权给第三方平台
- java开发微信第三方平台 模板消息发送消息案例
- 微信开放平台 公众号第三方平台开发 教程四 代公众号调用接口的SDK和demo
- 微信开放平台 公众号第三方平台开发 教程一 平台介绍
- 微信开放平台 公众号第三方平台开发 教程二 创建公众号第三方平台
- 微信开放平台 公众号第三方平台开发 教程一 平台介绍
- 微信开放平台之公众号第三方平台开发及全网发布验证
- 微信公众开放平台开发04---百度BAE,java应用部署服务器,jetty了解
- 微信开放平台 公众号第三方平台开发 教程二 创建公众号第三方平台
- 微信开放平台 公众号第三方平台开发 教程四 代公众号调用接口的SDK和demo
- 微信开放平台 公众号第三方平台开发 教程五 代公众号发起网页授权源码
- 微信开放平台 公众号第三方平台开发 教程一 平台介绍
- 微信开放平台之公众号第三方平台开发及全网发布验证
- 微信第三方平台开发中遇到的问题总结---java
- 微信公众开放平台开发08---纯java 实现微信开发:编写自定义菜单
- 微信开放平台 公众号第三方平台开发 教程三 一键登录授权给第三方平台
- 微信开放平台 公众号第三方平台开发 教程五 代公众号发起网页授权源码