发布时间:2025-12-09 15:54:18 浏览次数:4
开发第三方平台,首先需要到微信开放平台申请注册第三方平台账号
详细流程说明可参照官网授权流程技术说明;
出于安全考虑,在第三方平台创建审核通过后,微信服务器 每隔10分钟会向第三方的消息接收地址推送一次component_verify_ticket,用于获取第三方平台接口调用凭据。
package com.litte.controller.warrant;import com.litte.entity.reception.TWarrantInfo;import com.litte.service.warrantinfo.TWarrantInfoService;import com.litte.service.warrantmerchant.TWarrantMerchantService;import com.litte.util.DateUtil;import com.litte.util.WinxinUtil;import net.sf.json.JSONObject;import org.apache.commons.lang.StringUtils;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.DocumentHelper;import org.dom4j.Element;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.PathVariable;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.BufferedReader;import java.io.IOException;import java.io.PrintWriter;import java.util.Calendar;import java.util.Date;@Controller@RequestMapping("/warrant")public class WarrantController {private static final Logger LOGGER = LoggerFactory.getLogger(WarrantController.class);//公众号第三方平台的appidprivate static final String APPID = "wx9681884b28ed7927";//第三方平台申请时填写的接收消息的校验tokenprivate static final String TOKEN = "xinxingshang";// private static final String TOKEN = "xinxingshangstar";//第三方平台申请时填写的接收消息的加密symmetric_keyprivate static final String ENCODINGAESKEY = "xinxingshangstarkeykey11keyxinxingshangstar";private static final String SECRET = "a4d75ccc5b9ca0cef697116bc8c2e156";//获取"component_access_token"URLprivate static final String COMPONENT_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";// 获取"pre_auth_code"URLprivate static final String PRE_AUTH_CODE = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=componentAccessToken";// 刷新令牌private static final String AUTHORIZER_REFRESH_TOKEN = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=componentAccessToken";@AutowiredTWarrantInfoService tWarrantInfoService;@AutowiredTWarrantMerchantService TWarrantMerchantService;/*** 获取"component_verify_ticket"** @Description:* @Author: Mr.Jkx* @Date: 2019/4/3 16:03* 参考链接:https://blog.csdn.net/liaoyundababe/article/details/53537417* https://blog.csdn.net/zhangdaiscott/article/details/48269837*/@RequestMapping("/responseRequest")@ResponseBodypublic void responseRequest(HttpServletRequest request, HttpServletResponse response) throws AesException, IOException {LOGGER.info("WeChat third-party platform --------- WeChat push Ticket message 10 minutes-----------");output(response, "success"); // 输出响应的内容。processAuthorizeEvent(request);}/*** 处理授权事件的推送** @param request* @throws IOException* @throws AesException* @throws DocumentException*/public void processAuthorizeEvent(HttpServletRequest request)throws IOException, AesException {String nonce = request.getParameter("nonce");String timestamp = request.getParameter("timestamp");String msgSignature = request.getParameter("msg_signature");LOGGER.info("=====WeChat third-party platform======" + nonce + " " + timestamp + " " + msgSignature);StringBuilder sb = new StringBuilder();BufferedReader in = request.getReader();String line;while ((line = in.readLine()) != null) {sb.append(line);}String xml = sb.toString();LOGGER.info("Third-party platform is released on the whole network-----------------------original, Xml=" + xml);WXBizMsgCrypt pc = new WXBizMsgCrypt(TOKEN, ENCODINGAESKEY, APPID);LOGGER.info("Third-party platform is released on the whole network-----------------------decryption WXBizMsgCrypt new 成功");String xml1 = pc.decryptMsg(msgSignature, timestamp, nonce, xml);LOGGER.info("Third-party platform is released on the whole network-----------------------After decryption, Xml=" + xml1);processAuthorizationEvent(xml1);}/*** 保存componentVerifyTicket** @param xml*/void processAuthorizationEvent(String xml) {Document doc;try {doc = DocumentHelper.parseText(xml);Element rootElt = doc.getRootElement();String componentVerifyTicket = rootElt.elementText("ComponentVerifyTicket");LOGGER.info("Third-party platform is released on the whole network---------After decryption, ComponentVerifyTicket=" + componentVerifyTicket);if (StringUtils.isNotBlank(componentVerifyTicket)) {TWarrantInfo tWarrantInfo = new TWarrantInfo();tWarrantInfo.setId("053882ef3bed46c795bbd7da470e79cf");tWarrantInfo.setComponentVerifyTicket(componentVerifyTicket);tWarrantInfo.setTicketCreateTime(DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"));int i = tWarrantInfoService.updateByPrimaryKeySelective(tWarrantInfo);if (i > 0) {LOGGER.info("WeChat third-party platform------------component_verify_ticket data save success");} else {LOGGER.info("WeChat third-party platform------------component_verify_ticket data save failure");}}} catch (DocumentException e) {e.printStackTrace();}}/*** @Description: 第三方平台全网发布,消息与事件接收URL* @Author: Mr.Jkx* @Date: 2019/5/8 18:10*/@RequestMapping(value = "/$APPID$/xxsCallback")public void acceptMessageAndEvent(HttpServletRequest request, @PathVariable("APPID") String appid,HttpServletResponse response) throws IOException, DocumentException, AesException {LOGGER.info("第三方平台全网发布---------{appid}/callback---------验证开始。。。。");String msgSignature = request.getParameter("msg_signature");LOGGER.info("第三方平台全网发布-------------{appid}/callback-----------验证开始。。。。msg_signature=" + msgSignature);if (!StringUtils.isNotBlank(msgSignature)) {return;// 微信推送给第三方开放平台的消息一定是加过密的,无消息加密无法解密消息}StringBuilder sb = new StringBuilder();BufferedReader in = request.getReader();String line;while ((line = in.readLine()) != null) {sb.append(line);}in.close();String xml = sb.toString();Document doc = DocumentHelper.parseText(xml);Element rootElt = doc.getRootElement();String toUserName = rootElt.elementText("ToUserName");//微信全网测试账号// if (StringUtils.equalsIgnoreCase(toUserName, APPID)) {LOGGER.info("全网发布接入检测消息反馈开始---------------APPID=" + appid + "------------------------toUserName=" + toUserName);checkWeixinAllNetworkCheck(request, response, xml);// }}public void checkWeixinAllNetworkCheck(HttpServletRequest request, HttpServletResponse response,String xml) throws DocumentException, AesException, IOException {String nonce = request.getParameter("nonce");String timestamp = request.getParameter("timestamp");String msgSignature = request.getParameter("msg_signature");WXBizMsgCrypt pc = new WXBizMsgCrypt(TOKEN, ENCODINGAESKEY, APPID);xml = pc.decryptMsg(msgSignature, timestamp, nonce, xml);Document doc = DocumentHelper.parseText(xml);Element rootElt = doc.getRootElement();String msgType = rootElt.elementText("MsgType");String toUserName = rootElt.elementText("ToUserName");String fromUserName = rootElt.elementText("FromUserName");LOGGER.info("---全网发布接入检测--step.1-----------msgType="+msgType+"-----------------toUserName="+toUserName+"-----------------fromUserName="+fromUserName);// LogUtil.info("---全网发布接入检测--step.2-----------xml="+xml);if("event".equals(msgType)){// LogUtil.info("---全网发布接入检测--step.3-----------事件消息--------");String event = rootElt.elementText("Event");replyEventMessage(request,response,event,toUserName,fromUserName);}else if("text".equals(msgType)){// LogUtil.info("---全网发布接入检测--step.3-----------文本消息--------");String content = rootElt.elementText("Content");processTextMessage(request,response,content,toUserName,fromUserName);}}public void replyEventMessage(HttpServletRequest request, HttpServletResponse response, String event, String toUserName, String fromUserName) throws DocumentException, IOException {String content = event + "from_callback";// LogUtil.info("---全网发布接入检测------step.4-------事件回复消息 content="+content + " toUserName="+toUserName+" fromUserName="+fromUserName);replyTextMessage(request,response,content,toUserName,fromUserName);}public void processTextMessage(HttpServletRequest request, HttpServletResponse response,String content,String toUserName, String fromUserName) throws IOException, DocumentException{if("TESTCOMPONENT_MSG_TYPE_TEXT".equals(content)){String returnContent = content+"_callback";replyTextMessage(request,response,returnContent,toUserName,fromUserName);}else if(StringUtils.startsWithIgnoreCase(content, "QUERY_AUTH_CODE")){output(response, "");//接下来客服API再回复一次消息replyApiTextMessage(request,response,content.split(":")[1],fromUserName);}}/*** 回复微信服务器"文本消息"* @param request* @param response* @param content* @param toUserName* @param fromUserName* @throws DocumentException* @throws IOException*/public void replyTextMessage(HttpServletRequest request, HttpServletResponse response, String content, String toUserName, String fromUserName) throws DocumentException, IOException {Long createTime = Calendar.getInstance().getTimeInMillis() / 1000;StringBuffer sb = new StringBuffer();sb.append("<xml>");sb.append("<ToUserName><![CDATA["+fromUserName+"]]></ToUserName>");sb.append("<FromUserName><![CDATA["+toUserName+"]]></FromUserName>");sb.append("<CreateTime>"+createTime+"</CreateTime>");sb.append("<MsgType><![CDATA[text]]></MsgType>");sb.append("<Content><![CDATA["+content+"]]></Content>");sb.append("</xml>");String replyMsg = sb.toString();String returnvaleue = "";try {WXBizMsgCrypt pc = new WXBizMsgCrypt(TOKEN, ENCODINGAESKEY, APPID);returnvaleue = pc.encryptMsg(replyMsg, createTime.toString(), "easemob");// LOGGER.info("------------------加密后的返回内容 returnvaleue: "+returnvaleue);} catch (AesException e) {e.printStackTrace();}output(response, returnvaleue);}public void replyApiTextMessage(HttpServletRequest request, HttpServletResponse response, String auth_code, String fromUserName) throws DocumentException, IOException {// 得到微信授权成功的消息后,应该立刻进行处理!!相关信息只会在首次授权的时候推送过来LOGGER.info("------step.1----使用客服消息接口回复粉丝----逻辑开始-----------------");try {LOGGER.info("------step.1----使用客服消息接口回复粉丝----逻辑开始-------------auth_code: "+auth_code+" thirdWeixinService.getComponent_access_token:"+TOKEN);String url = "https://api.weixin.qq.com/cgi-bin/component/api_query_auth?component_access_token="+TOKEN;JSONObject jsonObject1 = new JSONObject();jsonObject1.put("component_appid", APPID);jsonObject1.put("authorization_code", auth_code);JSONObject jsonRes = WinxinUtil.doPostStr(url, jsonObject1.toString());LOGGER.info("------step.1----使用客服消息接口回复粉丝----逻辑开始---------------------jsonRes:"+jsonRes.toString());String msg = auth_code + "_from_api";JSONObject jsonObject = new JSONObject();jsonObject.put("touser", fromUserName);jsonObject.put("msgtype", "text");JSONObject text = new JSONObject();text.put("content", msg);jsonObject.put("text", text);WinxinUtil.doPostStr("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token="+TOKEN, jsonObject.toString());} catch (Exception e) {e.printStackTrace();}}/*** 工具类:回复微信服务器"文本消息"* @param response* @param returnvaleue*/public void output(HttpServletResponse response, String returnvaleue) {try {PrintWriter pw = response.getWriter();pw.write(returnvaleue);pw.flush();} catch (IOException e) {e.printStackTrace();}}}第三方平台通过自己的component_appid(即在微信开放平台管理中心的第三方平台详情页中的AppID和AppSecret)和component_appsecret,以及component_verify_ticket(每10分钟推送一次的安全ticket)来获取自己的接口调用凭据(component_access_token)
/*** @Description: “component_access_token”获取* 有效期2小时* @Author: Mr.Jkx* @Date: 2019/4/19 16:57* SpringBoot定时任务,每个110分钟获取一次,并保存到数据库*/@Scheduled(fixedRate = 6600000)public void getComponentAccessToken() {System.out.println("-------------get component_access_token---------------");WarrantController warrantController = new WarrantController();try {// 从数据库获取微信推送的“component_verify_ticket”TWarrantInfo tWarrantInfo = tWarrantInfoService.selectByPrimaryKey("053882ef3bed46c795bbd7da470e79cf");String componentVerifyTicket = tWarrantInfo.getComponentVerifyTicket();// 根据“componentVerifyTicket”获取“component_access_token”String componentAccessToken = getComponentAccessToken(componentVerifyTicket);// 数据库保存微信推送加密信息“component_access_token”TWarrantInfo tWarrantInfoData = new TWarrantInfo();tWarrantInfoData.setComponentAccessToken(componentAccessToken);editWarrantInfo(tWarrantInfoData);} catch (IOException e) {e.printStackTrace();}}/*** @Description: 编辑授权信息* @Author: Mr.Jkx* @Date: 2019/4/20 10:12*/public void editWarrantInfo(TWarrantInfo tWarrantInfo) {tWarrantInfo.setId("053882ef3bed46c795bbd7da470e79cf");if (StringUtils.isNotBlank(tWarrantInfo.getComponentAccessToken())) {tWarrantInfo.setTokenCreateTime(DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"));}if (StringUtils.isNotBlank(tWarrantInfo.getPreAuthCode())) {tWarrantInfo.setCodeCreateTime(DateUtil.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss"));}int i = tWarrantInfoService.updateByPrimaryKeySelective(tWarrantInfo);if (i > 0) {LOGGER.info("WeChat third-party platform------------data save success");} else {LOGGER.info("WeChat third-party platform------------data save failure");}}/*** @Description: 获取"component_access_token"* @Author: Mr.Jkx* @Date: 2019/4/9 9:45*/public String getComponentAccessToken(String componentVerifyTicket) throws IOException {/**拼装待发送的Json*/JSONObject json = new JSONObject();json.accumulate("component_appid", APPID);json.accumulate("component_appsecret", SECRET);json.accumulate("component_verify_ticket", componentVerifyTicket);LOGGER.info("第三方授权:获取component_access_token:getComponentAccessToken:请求参数json={}", json);/**发送Https请求到微信*/JSONObject retJSONObject = WinxinUtil.doPostStr(COMPONENT_TOKEN_URL, json.toString());String component_access_token = retJSONObject.getString("component_access_token");LOGGER.info("第三方授权:获取component_access_token回执数据:getComponentAccessToken:component_access_token={}", component_access_token);return component_access_token;}第三方平台通过自己的接口调用凭据(component_access_token)来获取用于授权流程准备的预授权码(pre_auth_code)
/*** @Description: 获取预授权码“pre_auth_code”* 有效期10分钟* @Author: Mr.Jkx* @Date: 2019/4/19 16:57* SpringBoot定时任务,每隔9分钟获取一次,并保存到数据库*/@Scheduled(fixedRate = 540000)public void getpreAuthCode() {System.out.println("---------------get pre_auth_code-------------");WarrantController warrantController = new WarrantController();try {// 从数据库获取“component_access_token”令牌TWarrantInfo tWarrantInfo = tWarrantInfoService.selectByPrimaryKey("053882ef3bed46c795bbd7da470e79cf");String component_access_token = tWarrantInfo.getComponentAccessToken();// 根据“component_access_token”获取“pre_auth_code”String preAuthCodeData = getPreAuthCodeData(component_access_token);// 数据库保存预授权码“pre_auth_code”TWarrantInfo tWarrantInfoData = new TWarrantInfo();tWarrantInfoData.setPreAuthCode(preAuthCodeData);editWarrantInfo(tWarrantInfoData);} catch (IOException e) {e.printStackTrace();}}/*** @Description: 获取"pre_auth_code"(预授权码)* @Author: Mr.Jkx* @Date: 2019/4/9 10:21*/public String getPreAuthCodeData(String component_access_token) throws IOException {JSONObject json = new JSONObject();json.accumulate("component_appid", APPID);json.accumulate("component_access_token", component_access_token);String url = PRE_AUTH_CODE.replace("componentAccessToken", component_access_token);/**发送Https请求到微信*/JSONObject retStr = WinxinUtil.doPostStr(url, json.toString());LOGGER.info("第三方授权:ThirdPartyServiceImpl:getPreAuthCode:retStr={}", retStr);JSONObject retJSONObject = JSONObject.fromObject(retStr);/**在返回结果中获取pre_auth_code*/String pre_auth_code = retJSONObject.getString("pre_auth_code");LOGGER.info("==========第三方授权:获取pre_auth_code:{}", pre_auth_code);return pre_auth_code;}通过授权码和自己的接口调用凭据(component_access_token),换取公众号或小程序的接口调用凭据(authorizer_access_token和用于前者快过期时用来刷新它的authorizer_refresh_token)和授权信息(授权了哪些权限等信息)
/*** @Description: 刷新令牌“authorizer_refresh_token”* 有效期2小时* @Author: Mr.Jkx* @Date: 2019/4/19 16:57*/@Scheduled(fixedRate = 6600000)public void refreshAuthorizerRefreshToken() {System.out.println("---------------get authorizer_refresh_token-------------");WarrantController warrantController = new WarrantController();try {// 授权方appid{},接口调用凭据刷新令牌查询List<TWarrantMerchant> tWarrantMerchantList = TWarrantMerchantService.selTWarrantMerchant();// 从数据库获取“component_access_token”令牌TWarrantInfo tWarrantInfo = tWarrantInfoService.selectByPrimaryKey("053882ef3bed46c795bbd7da470e79cf");String component_access_token = tWarrantInfo.getComponentAccessToken();List<TWarrantMerchant> tWarrantMerchants = refreshAuthorizerRefreshToken(tWarrantMerchantList, component_access_token);if (tWarrantMerchants.size() > 0) {for (TWarrantMerchant tWarrantMerchant : tWarrantMerchants) {long currentTime = new Date().getTime() + 120 * 60 * 1000;Date date = new Date(currentTime);tWarrantMerchant.setDeadline(date);TWarrantMerchantService.updTWarrantMerchant(tWarrantMerchant);}} else {System.out.println("---------------No authorizer_refresh_token needs to updated-------------");}} catch (IOException e) {e.printStackTrace();}}/*** @Description: 刷新令牌“authorizer_refresh_token”* @Author: Mr.Jkx* @Date: 2019/5/6 17:58*/public List<TWarrantMerchant> refreshAuthorizerRefreshToken(List<TWarrantMerchant> tWarrantMerchants, String component_access_token) throws IOException {List<TWarrantMerchant> tWarrantMerchantList = new ArrayList<>();// 查找需要刷新令牌的数据for (TWarrantMerchant tWarrantMerchant : tWarrantMerchants) {// Date deadline = tWarrantMerchant.getDeadline();// if (deadline.getTime() - new Date().getTime() < 600000) {tWarrantMerchantList.add(tWarrantMerchant);// }}// 重新获取令牌List<TWarrantMerchant> authorizerRefreshToken = getAuthorizerRefreshToken(tWarrantMerchantList, component_access_token);return authorizerRefreshToken;}/*** @Description: 重新获取令牌* @Author: Mr.Jkx* @Date: 2019/5/6 18:39*/private List<TWarrantMerchant> getAuthorizerRefreshToken(List<TWarrantMerchant> tWarrantMerchantList, String component_access_token) throws IOException {List<TWarrantMerchant> tWarrantMerchants = new ArrayList<>();for (TWarrantMerchant tWarrantMerchant : tWarrantMerchantList) {JSONObject json = new JSONObject();json.accumulate("component_appid", APPID);json.accumulate("authorizer_appid", tWarrantMerchant.getAuthorizationAppid());json.accumulate("authorizer_refresh_token", tWarrantMerchant.getAuthorizerRefreshToken());/**发送Https请求到微信*/String url = AUTHORIZER_REFRESH_TOKEN.replace("componentAccessToken", component_access_token);JSONObject retStr = WinxinUtil.doPostStr(url, json.toString());LOGGER.info("==========第三方授权: 令牌刷新{}", retStr);JSONObject retJSONObject = JSONObject.fromObject(retStr);/**在返回结果中获取pre_auth_code*/LOGGER.info("==========第三方授权:令牌刷新:{}", retJSONObject);String authorizer_refresh_token = retJSONObject.getString("authorizer_refresh_token");// 接口调用凭据刷新令牌String authorizer_access_token = retJSONObject.getString("authorizer_access_token");// 小程序授权令牌LOGGER.info("==========第三方授权:获取到的新令牌:{}", retJSONObject);// 授权商户信息保存TWarrantMerchant updTWarrantMerchant = new TWarrantMerchant();updTWarrantMerchant.setId(tWarrantMerchant.getId());// 接口调用凭据刷新令牌updTWarrantMerchant.setAuthorizerRefreshToken(authorizer_refresh_token);updTWarrantMerchant.setAuthorizerAccessToken(authorizer_access_token);tWarrantMerchants.add(updTWarrantMerchant);}return tWarrantMerchants;}准备好授权所需的必要信息,请求授权页面,扫码授权。
请求获取授权信息,及授权小程序信息;