微信開發(4):微信第三方開放平臺的搭建(java)
什麼是第三方開放平臺
來波官方解釋:
我才是官方文件
第三方平臺的開放,讓公眾號或小程式運營者在面向垂直行業需求時,可以通過一鍵登入授權給第三方開發者,來完成相關能力。
簡單的說,就是讓公眾號授權給第三個開放平臺,根據授權不同,第三開放平臺可以獲取到該公眾號的介面許可權,從而直接呼叫微信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 = 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小時 需要快取。最後上一波成功圖片
相關文章
- 微信開放平臺
- 微信開放平臺 第三方平臺開發踩坑記錄
- 微信開放平臺高效開發除錯方法除錯
- 微信開放平臺-第三方平臺-全網釋出邏輯
- 微信開放平臺手機APP支付APP
- 成為騰訊開發者——如何使用QQ開放平臺和微信開放平臺
- 微信公共平臺開發(轉)
- 使用 TypeScript 開發 Node.js 的微信開放平臺/企業微信/釘釘開放平臺訊息 AES 加密解密庫並且釋出TypeScriptNode.js加密解密
- 微信公眾平臺搭建與開發揭祕.pdf
- ThinkPhp3.2.1開發的微信平臺PHP
- 微信開發之微信支付(商戶平臺提供的方式)
- 解讀微信第三方平臺-代小程式開發
- 4.PHP微信公眾平臺開發 - 簡單回覆功能開發PHP
- 微信公眾平臺的不斷對外開放使微信創業迎來全面爆發創業
- 微信公眾平臺開發入門
- java 微信開發Java
- Senparc.Weixin.MP SDK 微信公眾平臺開發教程(三):微信公眾平臺開發驗證
- java開發微信公眾平臺(一)-- 伺服器配置Java伺服器
- 微信公眾平臺開發(六) 翻譯功能開發
- 基於 PHP 的微信公眾平臺開發PHP
- 微信開發系列之一 - 微信公眾號開發的開發環境搭建開發環境
- PHP微信公眾平臺開發視訊PHP
- 微信公眾平臺開發(十一) 功能整合
- 微信公眾平臺完整開發教程 by ZTalk
- 微信公眾平臺開發(81) 2014新年微信賀卡
- 微信公眾平臺開發(四) 簡單回覆功能開發
- 簡單獲取安卓應用簽名(微信開放平臺)安卓
- 微信開放文件地址
- 微信公眾平臺開發之店鋪類
- 微信行銷平臺快速生成開發框架—KyPHP框架PHP
- 線上教育平臺微信小程式如何開發建立?微信小程式
- 6.PHP微信公眾平臺開發 - 翻譯功能開發PHP
- 微信公眾平臺開發(五) 天氣預報功能開發
- 微信公眾平臺開發(七) 聊天機器人功能開發機器人
- 微信公眾平臺開發(八) 自定義選單功能開發
- 微信Java SDK開發文件Java
- 微信開發 微信支付
- ThinkPHP 3.2.2開發微信多使用者管理平臺PHP