最近老板下发了个任务,有一个业务是整合用户的微信公众号(服务号),然后在我们的erp系统里给下家客户发送模板消息,找了一下发现微信第三方平台可以实现,那就干起来。
先在微信开放平台,申请一个第三方平台服务。
然后通过下面几步实现消息推送1 、微信验证接口,拿到 ticket
2 、拿到调用令牌 component_access_token
3 、获取预授权码 pre_auth_code
4 、扫码授权,获取授权码 auth_code
5 、通过授权码拿到调用令牌(authorizer_access_token)和刷新令牌(authorizer_refresh_token)
6 、通过 authorizer_access_token 实现消息推送
1、验证接口代码
@RequestMapping("getTicket")
public String componentVerifyTicket(HttpServletRequest request, HttpServletResponse response) throws Exception{
log.info("接收component_verify_ticket 或 authorized事件");
String nonce = request.getParameter("nonce");
String timestamp = request.getParameter("timestamp");
String msgSignature = request.getParameter("msg_signature");
StringBuilder sb = new StringBuilder();
BufferedReader in = request.getReader();
String line;
while((line = in.readLine()) != null) {
sb.append(line);
}
String postData = sb.toString();
log.info("nonce: " + nonce);
log.info("timestamp: " + timestamp);
log.info("msgSignature: " + msgSignature);
log.info("postData: " + postData);
getComponentVerifyTicket(timestamp, nonce, msgSignature, postData);
return "success";
}
public static void getComponentVerifyTicket(String timestamp, String nonce, String msgSignature, String postData) throws Exception {
// 需要加密的明文
// COMPONENT_TOKEN token
// aesKey 加密密钥
WXBizMsgCrypt pc = new WXBizMsgCrypt(COMPONENT_TOKEN, aesKey, appId);
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
dbf.setXIncludeAware(false);
dbf.setExpandEntityReferences(false);
DocumentBuilder db = dbf.newDocumentBuilder();
StringReader sr = new StringReader(postData);
InputSource is = new InputSource(sr);
Document document = db.parse(is);
Element root = document.getDocumentElement();
NodeList nodelist1 = root.getElementsByTagName("Encrypt");
String encrypt = nodelist1.item(0).getTextContent();
String format = "<xml><ToUserName><![CDATA[toUser]]></ToUserName><Encrypt><![CDATA[%1$s]]></Encrypt></xml>";
String fromXML = String.format(format, encrypt);
String result = pc.decryptMsg(msgSignature, timestamp, nonce, fromXML);
log.info("解密后: " + result);
Map<String, String> map = XMLUtil.doXMLParse(result);
String componentVerifyTicket = map.get("ComponentVerifyTicket");
System.out.println("获取ticket====="+componentVerifyTicket);
}
XMLUtil
/**
* 解析xml,返回第一级元素键值对。如果第一级元素有子节点,则此节点的值是子节点的xml数据。
*
* @param strxml
* @return
* @throws JDOMException
* @throws IOException
*/
public static Map doXMLParse(String strxml) throws JDOMException, IOException {
strxml = strxml.replaceFirst("encoding=\".*\"", "encoding=\"UTF-8\"");
if (null == strxml || "".equals(strxml)) {
return null;
}
Map m = new HashMap();
InputStream in = new ByteArrayInputStream(strxml.getBytes("UTF-8"));
SAXBuilder builder = new SAXBuilder();
Document doc = builder.build(in);
Element root = doc.getRootElement();
List list = root.getChildren();
Iterator it = list.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String k = e.getName();
String v = "";
List children = e.getChildren();
if (children.isEmpty()) {
v = e.getTextNormalize();
} else {
v = XMLUtil.getChildrenText(children);
}
m.put(k, v);
}
//关闭流
in.close();
return m;
}
/**
* 获取子结点的xml
*
* @param children
* @return String
*/
public static String getChildrenText(List children) {
StringBuffer sb = new StringBuffer();
if (!children.isEmpty()) {
Iterator it = children.iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
String name = e.getName();
String value = e.getTextNormalize();
List list = e.getChildren();
sb.append("<" + name + ">");
if (!list.isEmpty()) {
sb.append(XMLUtil.getChildrenText(list));
}
sb.append(value);
sb.append("</" + name + ">");
}
}
return sb.toString();
}
2、拿到调用令牌 component_access_token
微信文档链接:
https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/ticket-token/getComponentAccessToken.html
通过上一步拿到的ticket,传入第三方的appid和appsecret ,调用微信接口获取第三方的调用令牌:
component_access_token
https://api.weixin.qq.com/cgi-bin/component/api_component_token
{
"component_appid": "*****" , // 第三方平台appid
"component_appsecret": "*****", // 密钥
"component_verify_ticket":""
}
3、拿到component_access_token后再次调用接口获取预授权码
微信文档:
https://developers.weixin.qq.com/doc/oplatform/openApi/OpenApiDoc/ticket-token/getPreAuthCode.html
ACCESS_TOKEN: 就是上一步获取到的component_access_token
https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?access_token=ACCESS_TOKEN
{
"component_appid": "wxe7986597a6b2a7e4"
}
4、通过预授权码生成链接,管理员扫码后授权获取授权码
将上一步获取到的预授权吗填入下面:
https://mp.weixin.qq.com/cgi-bin/componentloginpage?
component_appid=wxe7986597a6b2a7e4
&pre_auth_code=preauthcode@@@IbLBSSV_2LkGjtZKk8aPCFc4JI2Wzzawfj6tse60aktFdrTD1oP_nWNULntkhPjHKvp3Ae1GnL4sReu_7A2zjA
&redirect_uri=http://ys99.top/bed/wx/getAuthCode
component_appid:第三方平台的appid
pre_auth_code:上一步拿到的预授权码
redirect_uri:授权后重定向地址,接口里拿到正式的授权码 queryauthcode
然后通过a链接打开就可以了
5、通过授权码获取第三方公众号的授权令牌 authorizer_access_token
https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token=******
{
"component_appid":"wxe7986597a6b2a7e4",
"authorization_code":"queryauthcode@@@qCsx1osdrsQRn7LoiLVOIz3Ut273KrqYJlT9cdbQS3lCupjsPvFcaAbaT3zHG2EDhCgJ3dUw94gbgrOOTFhl_g"
}
6、通过上一步获取到了公众号的调用令牌authorizer_access_token
后续的公众号所有操作都使用这个令牌https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=
66_Ew0rFnSUMD56EEsZBk8aVTteynjtjojSliKpAIvBd8AR750VvN0O3puNI2srbPAIlkesBainkEBJTT4dRQWVQbxf2wuFB_om-mgJ-AXxZ26gJKjIAdPpNIQzkUOX9x8TJllWGBqU2Fr7oR16OCNaAHDAOZ
access_token:就是上一步获取到的调用令牌
touser:用户在微信公众号的openid
template_id:在公众号的消息模板id