日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

微信第三方平台开发

發布時間:2023/12/10 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 微信第三方平台开发 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

    • 需求
    • 實現
      • 步驟:獲取component_verify_ticket——>獲取component_access_token——>獲取authorizer_access_token——>調接口發布小程序
      • 1、獲取component_verify_ticket
        • controller
        • 實現類
      • 2、獲取component_access_token
        • 實現類
      • 3、獲取authorizer_access_token
        • 步驟:
        • 獲取預授權碼pre_auth_code——>微信公眾平臺管理員授權——>獲取authorizer_access_token&authorizer_refresh_token
        • ①獲取預授權碼pre_auth_code
        • ②微信公眾平臺管理員授權
        • ③獲取authorizer_access_token&authorizer_refresh_token
      • 4、調接口發布小程序
        • 提交代碼接口:[上傳小程序代碼并生成體驗版 | 微信開放文檔 (qq.com)](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/commit.html)
        • 設置用戶隱私接口:[配置小程序用戶隱私保護指引 | 微信開放文檔 (qq.com)](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/privacy_config/set_privacy_setting.html)
        • 提交審核接口:[提交審核 | 微信開放文檔 (qq.com)](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/submit_audit.html)
        • 查詢審核結果接口:[查詢最新一次提交的審核狀態 | 微信開放文檔 (qq.com)](https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/code/get_latest_auditstatus.html)
    • 最后

需求

根據現有的平臺小程序,快速發布一個相同類型的小程序

如果按常規的做法每個小程序都需要經歷這樣的步驟:在公眾平臺申請appid——>拉代碼到自己的倉庫——>打開編譯器綁定自己的appId——>上傳代碼到公眾平臺——>再提交審核——>在公眾平臺調整開發設置。

如果用微信開放平臺的服務平臺開發的步驟:在微信開放平臺申請第三方服務平臺——>上傳小程序模板(只有第一次有)——>調接口上傳小程序代碼

——>調接口提交審核——>調接口發布小程序。

上傳小程序模板的步驟:在公眾平臺申請appid——>在微信開放平臺的第三方服務平臺中添加開發小程序——>上傳小程序代碼(此時會直接上傳到了開放平臺的草稿箱)——>將草稿箱的代碼作為普通模板

這就是服務平臺開發平臺的便捷,只需上傳一次小程序代碼,就可以快速的發布小程序。

實現

步驟:獲取component_verify_ticket——>獲取component_access_token——>獲取authorizer_access_token——>調接口發布小程序

1、獲取component_verify_ticket

官方文檔:驗證票據 | 微信開放文檔 (qq.com)

因為這個驗證票據是微信官方主動推送的,所以需要在第三方平臺配置一波

controller

@ApiOperation(value = "微信開放平臺:授權事件接收URL,驗證票據", notes = "zhuguangliang") @AnonymousPostMapping("/pushTicket") public String wechatPlatformEvent(@RequestParam("timestamp") String timestamp,@RequestParam("nonce") String nonce,@RequestParam("msg_signature") String msgSignature,@RequestBody String postData) {return openPlatformUtil.parseRequest(timestamp, nonce, msgSignature, postData); }

實現類

關于存儲component_verify_ticket方案,我這里是mysql和redis各存一份。

public String parseRequest(String timeStamp, String nonce, String msgSignature, String postData) {try {if (redisUtils.hasKey(CacheKey.OPEN_PLATFORM_TICKET)) {return "success";}//這個類是微信官網提供的解密類,需要用到消息校驗Token 消息加密Key和服務平臺appidWXBizMsgCrypt pc = new WXBizMsgCrypt(Token, Key, APPID);String xml = pc.decryptMsg(msgSignature, timeStamp, nonce, postData);Map<String, String> result = WXXmlToMapUtil.xmlToMap(xml);// 將xml轉為mapString componentVerifyTicket = result.get("ComponentVerifyTicket");if (StringUtils.isNotEmpty(componentVerifyTicket)) {// 存儲平臺授權票據,保存ticketSpiritOpenPlatform spiritOpenPlatform = new SpiritOpenPlatform();if (platformMapper.selectByAppId(APPID) == 0) {spiritOpenPlatform.setAppid(APPID);spiritOpenPlatform.setAppSecret(AppSecret);spiritOpenPlatform.setPlatformKey(Key);spiritOpenPlatform.setToken(Token);spiritOpenPlatform.setComponentVerifyTicket(componentVerifyTicket);platformMapper.insert(spiritOpenPlatform);} else {spiritOpenPlatform.setComponentVerifyTicket(componentVerifyTicket);LambdaUpdateWrapper<SpiritOpenPlatform> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(SpiritOpenPlatform::getAppid, APPID);platformMapper.update(spiritOpenPlatform, updateWrapper);}redisUtils.set(CacheKey.OPEN_PLATFORM_TICKET, componentVerifyTicket, 60 * 60 * 12);log.info("微信開放平臺,第三方平臺獲取【驗證票據】成功");} else {log.error("微信開放平臺,第三方平臺獲取【驗證票據】失敗");}} catch (AesException e) {log.error("微信開放平臺,第三方平臺獲取【驗證票據】失敗,異常信息:" + e.getMessage());}return "success";}

官方提供的解密類:WXBizMsgCrypt.java

import org.apache.commons.codec.binary.Base64; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import org.xml.sax.InputSource;import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.StringReader; import java.nio.charset.Charset; import java.security.MessageDigest; import java.util.Arrays;/*** 提供接收和推送給公眾平臺消息的加解密接口(UTF8編碼的字符串).* <ol> * <li>第三方回復加密消息給公眾平臺</li> * <li>第三方收到公眾平臺發送的消息,驗證消息的安全性,并對消息進行解密。</li>* </ol>* 說明:異常java.security.InvalidKeyException:illegal Key Size的解決方案* <ol>* <li>在官方網站下載JCE無限制權限策略文件(JDK7的下載地址: ** http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>* <li>下載后解壓,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>* <li>如果安裝了JRE,將兩個jar文件放到%JRE_HOME%\lib\security目錄下覆蓋原來的文件</li>* <li>如果安裝了JDK,將兩個jar文件放到%JDK_HOME%\jre\lib\security目錄下覆蓋原來文件</li>** </ol>*/ public class WXBizMsgCrypt {static Charset CHARSET = Charset.forName("utf-8");Base64 base64 = new Base64();byte[] aesKey;String token;String appId;/*** 構造函數** @param token 公眾平臺上,開發者設置的token* @param encodingAesKey 公眾平臺上,開發者設置的EncodingAESKey* @param appId 公眾平臺appid* @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息*/public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException {if (encodingAesKey.length() != 43) {throw new AesException(AesException.IllegalAesKey);}this.token = token;this.appId = appId;aesKey = Base64.decodeBase64(encodingAesKey + "=");}// 還原4個字節的網絡字節序int recoverNetworkBytesOrder(byte[] orderBytes) {int sourceNumber = 0;for (int i = 0; i < 4; i++) {sourceNumber <<= 8;sourceNumber |= orderBytes[i] & 0xff;}return sourceNumber;}/*** 對密文進行解密.** @param text 需要解密的密文* @return 解密得到的明文* @throws AesException aes解密失敗*/String decrypt(String text) throws AesException {byte[] original;try {// 設置解密模式為AES的CBC模式Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);// 使用BASE64對密文進行解碼byte[] encrypted = Base64.decodeBase64(text);// 解密original = cipher.doFinal(encrypted);} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.DecryptAESError);}String xmlContent, from_appid;try {// 去除補位字符byte[] bytes = PKCS7Encoder.decode(original);// 分離16位隨機字符串,網絡字節序和AppIdbyte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);int xmlLength = recoverNetworkBytesOrder(networkOrder);xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);from_appid =new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length), CHARSET);} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.IllegalBuffer);}// appid不相同的情況if (!from_appid.equals(appId)) {throw new AesException(AesException.ValidateSignatureError);}return xmlContent;}/*** * 檢驗消息的真實性,并且獲取解密后的明文.* <ol>* <li>利用收到的密文生成安全簽名,進行簽名驗證</li>* <li>若驗證通過,則提取xml中的加密消息</li>* <li>對消息進行解密</li>* </ol>** @param msgSignature 簽名串,對應URL參數的msg_signature* @param timeStamp 時間戳,對應URL參數的timestamp* @param nonce 隨機串,對應URL參數的nonce* @param postData 密文,對應POST請求的數據* @return 解密后的原文* @throws AesException 執行失敗,請查看該異常的錯誤碼和具體的錯誤信息*/public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData)throws AesException {// 密鑰,公眾賬號的app secret// 提取密文Object[] encrypt = extract(postData);// 驗證安全簽名String signature = getSHA1(token, timeStamp, nonce, encrypt[1].toString());// 和URL中的簽名比較是否相等// System.out.println("第三方收到URL中的簽名:" + msg_sign);// System.out.println("第三方校驗簽名:" + signature);if (!signature.equals(msgSignature)) {throw new AesException(AesException.ValidateSignatureError);}// 解密String result = decrypt(encrypt[1].toString());return result;}/*** 提取出xml數據包中的加密消息** @param xmltext 待提取的xml字符串* @return 提取出的加密消息字符串* @throws AesException*/public static Object[] extract(String xmltext) throws AesException {Object[] result = new Object[3];try {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(xmltext);InputSource is = new InputSource(sr);Document document = db.parse(is);Element root = document.getDocumentElement();NodeList nodelist1 = root.getElementsByTagName("Encrypt");NodeList nodelist2 = root.getElementsByTagName("ToUserName");result[0] = 0;result[1] = nodelist1.item(0).getTextContent();//注意這里,獲取ticket中的xml里面沒有ToUserName這個元素,官網原示例代碼在這里會報空//空指針,所以需要處理一下if (nodelist2 != null) {if (nodelist2.item(0) != null) {result[2] = nodelist2.item(0).getTextContent();}}return result;} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.ParseXmlError);}}/*** 用SHA1算法生成安全簽名** @param token 票據* @param timestamp 時間戳* @param nonce 隨機字符串* @param encrypt 密文* @return 安全簽名* @throws AesException*/public static String getSHA1(String token, String timestamp, String nonce, String encrypt)throws AesException {try {String[] array = new String[]{token, timestamp, nonce, encrypt};StringBuffer sb = new StringBuffer();// 字符串排序Arrays.sort(array);for (int i = 0; i < 4; i++) {sb.append(array[i]);}String str = sb.toString();// SHA1簽名生成MessageDigest md = MessageDigest.getInstance("SHA-1");md.update(str.getBytes());byte[] digest = md.digest();StringBuffer hexstr = new StringBuffer();String shaHex = "";for (int i = 0; i < digest.length; i++) {shaHex = Integer.toHexString(digest[i] & 0xFF);if (shaHex.length() < 2) {hexstr.append(0);}hexstr.append(shaHex);}return hexstr.toString();} catch (Exception e) {e.printStackTrace();throw new AesException(AesException.ComputeSignatureError);}} }

PKCS7Encode.java

import java.nio.charset.Charset; import java.util.Arrays;/*** 提供基于PKCS7算法的加解密接口.*/ public class PKCS7Encoder {static Charset CHARSET = Charset.forName("utf-8");static int BLOCK_SIZE = 32;/*** 獲得對明文進行補位填充的字節.** @param count 需要進行填充補位操作的明文字節個數* @return 補齊用的字節數組*/static byte[] encode(int count) {// 計算需要填充的位數int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);if (amountToPad == 0) {amountToPad = BLOCK_SIZE;}// 獲得補位所用的字符char padChr = chr(amountToPad);String tmp = new String();for (int index = 0; index < amountToPad; index++) {tmp += padChr;}return tmp.getBytes(CHARSET);}/*** 刪除解密后明文的補位字符** @param decrypted 解密后的明文* @return 刪除補位字符后的明文*/static byte[] decode(byte[] decrypted) {int pad = (int) decrypted[decrypted.length - 1];if (pad < 1 || pad > 32) {pad = 0;}return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);}/*** 將數字轉化成ASCII碼對應的字符,用于對明文進行補碼** @param a 需要轉化的數字* @return 轉化得到的字符*/static char chr(int a) {byte target = (byte) (a & 0xFF);return (char) target;} }

WXXmlToMapUtil.java

import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.OutputFormat; import org.dom4j.io.SAXReader; import org.dom4j.io.XMLWriter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Node; import org.w3c.dom.NodeList;import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.io.StringReader; import java.io.StringWriter; import java.util.HashMap; import java.util.List; import java.util.Map;public class WXXmlToMapUtil {private static final Logger logger = LoggerFactory.getLogger(WXXmlToMapUtil.class);/*** XML格式字符串轉換為Map** @param xml XML字符串* @return XML數據轉換后的Map*/public static Map<String, String> xmlToMap(String xml) {try {Map<String, String> data = new HashMap<>();DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();InputStream stream = new ByteArrayInputStream(xml.getBytes("UTF-8"));org.w3c.dom.Document doc = documentBuilder.parse(stream);doc.getDocumentElement().normalize();NodeList nodeList = doc.getDocumentElement().getChildNodes();for (int idx = 0; idx < nodeList.getLength(); ++idx) {Node node = nodeList.item(idx);if (node.getNodeType() == Node.ELEMENT_NODE) {org.w3c.dom.Element element = (org.w3c.dom.Element) node;data.put(element.getNodeName(), element.getTextContent());}}stream.close();return data;} catch (Exception e) {e.printStackTrace();return null;}}/*** 將Map轉換為XML格式的字符串** @param data Map類型數據* @return XML格式的字符串*/public static String mapToXml(Map<String, String> data) throws Exception {try {DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();org.w3c.dom.Document document = documentBuilder.newDocument();org.w3c.dom.Element root = document.createElement("xml");document.appendChild(root);for (String key : data.keySet()) {String value = data.get(key);if (value == null) {value = "";}value = value.trim();org.w3c.dom.Element filed = document.createElement(key);filed.appendChild(document.createTextNode(value));root.appendChild(filed);}TransformerFactory tf = TransformerFactory.newInstance();Transformer transformer = tf.newTransformer();DOMSource source = new DOMSource(document);transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");transformer.setOutputProperty(OutputKeys.INDENT, "yes");StringWriter writer = new StringWriter();StreamResult result = new StreamResult(writer);transformer.transform(source, result);String output = writer.getBuffer().toString(); //.replaceAll("\n|\r", "");writer.close();return output;} catch (Exception e) {e.printStackTrace();return null;}}/*** (多層)xml格式字符串轉換為map** @param xml xml字符串* @return 第一個為Root節點,Root節點之后為Root的元素,如果為多層,可以通過key獲取下一層Map*/public static Map<String, Object> multilayerXmlToMap(String xml) {Document doc = null;try {doc = DocumentHelper.parseText(xml);} catch (DocumentException e) {logger.error("xml字符串解析,失敗 --> {}", e);}Map<String, Object> map = new HashMap<>();if (null == doc) {return map;}// 獲取根元素Element rootElement = doc.getRootElement();recursionXmlToMap(rootElement, map);return map;}/*** multilayerXmlToMap核心方法,遞歸調用** @param element 節點元素* @param outmap 用于存儲xml數據的map*/private static void recursionXmlToMap(Element element, Map<String, Object> outmap) {// 得到根元素下的子元素列表List<Element> list = element.elements();int size = list.size();if (size == 0) {// 如果沒有子元素,則將其存儲進map中outmap.put(element.getName(), element.getTextTrim());} else {// innermap用于存儲子元素的屬性名和屬性值Map<String, Object> innermap = new HashMap<>();// 遍歷子元素list.forEach(childElement -> recursionXmlToMap(childElement, innermap));outmap.put(element.getName(), innermap);}}/*** (多層)map轉換為xml格式字符串** @param map 需要轉換為xml的map* @param isCDATA 是否加入CDATA標識符 true:加入 false:不加入* @return xml字符串*/public static String multilayerMapToXml(Map<String, Object> map, boolean isCDATA) {String parentName = "xml";Document doc = DocumentHelper.createDocument();doc.addElement(parentName);String xml = recursionMapToXml(doc.getRootElement(), parentName, map, isCDATA);return formatXML(xml);}/*** multilayerMapToXml核心方法,遞歸調用** @param element 節點元素* @param parentName 根元素屬性名* @param map 需要轉換為xml的map* @param isCDATA 是否加入CDATA標識符 true:加入 false:不加入* @return xml字符串*/private static String recursionMapToXml(Element element, String parentName, Map<String, Object> map, boolean isCDATA) {Element xmlElement = element.addElement(parentName);map.keySet().forEach(key -> {Object obj = map.get(key);if (obj instanceof Map) {recursionMapToXml(xmlElement, key, (Map<String, Object>) obj, isCDATA);} else {String value = obj == null ? "" : obj.toString();if (isCDATA) {xmlElement.addElement(key).addCDATA(value);} else {xmlElement.addElement(key).addText(value);}}});return xmlElement.asXML();}/*** 格式化xml,顯示為容易看的XML格式** @param xml 需要格式化的xml字符串*/public static String formatXML(String xml) {String requestXML = null;try {// 拿取解析器SAXReader reader = new SAXReader();Document document = reader.read(new StringReader(xml));if (null != document) {StringWriter stringWriter = new StringWriter();// 格式化,每一級前的空格OutputFormat format = new OutputFormat(" ", true);// xml聲明與內容是否添加空行format.setNewLineAfterDeclaration(false);// 是否設置xml聲明頭部format.setSuppressDeclaration(false);// 是否分行format.setNewlines(true);XMLWriter writer = new XMLWriter(stringWriter, format);writer.write(document);writer.flush();writer.close();requestXML = stringWriter.getBuffer().toString();}return requestXML;} catch (Exception e) {logger.error("格式化xml,失敗 --> {}", e);return null;}} }

AesException.java

@SuppressWarnings("serial") public class AesException extends Exception {public final static int OK = 0;public final static int ValidateSignatureError = -40001;public final static int ParseXmlError = -40002;public final static int ComputeSignatureError = -40003;public final static int IllegalAesKey = -40004;public final static int ValidateCorpidError = -40005;public final static int EncryptAESError = -40006;public final static int DecryptAESError = -40007;public final static int IllegalBuffer = -40008;private int code;private static String getMessage(int code) {switch (code) {case ValidateSignatureError:return "簽名驗證錯誤";case ParseXmlError:return "xml解析失敗";case ComputeSignatureError:return "sha加密生成簽名失敗";case IllegalAesKey:return "SymmetricKey非法";case ValidateCorpidError:return "corpid校驗失敗";case EncryptAESError:return "aes加密失敗";case DecryptAESError:return "aes解密失敗";case IllegalBuffer:return "解密后得到的buffer非法";default:return null; // cannot be}}public int getCode() {return code;}AesException(int code) {super(getMessage(code));this.code = code;}}

2、獲取component_access_token

官方文檔:令牌 | 微信開放文檔 (qq.com)

實現類

@Override public void getComponentAccessToken() {String componentAccessTokenUrl = "https://api.weixin.qq.com/cgi-bin/component/api_component_token";if (redisUtils.hasKey(CacheKey.COMPONENT_ACCESS_TOKEN)) {if (redisUtils.getExpire(CacheKey.COMPONENT_ACCESS_TOKEN) > 10 * 60) {return;}}JSONObject params = new JSONObject();//第三方平臺的appid、appsecretparams.put("component_appid", APPID);params.put("component_appsecret", AppSecret);String component_verify_ticket;if (redisUtils.hasKey(CacheKey.OPEN_PLATFORM_TICKET)) {component_verify_ticket = (String) redisUtils.get(CacheKey.OPEN_PLATFORM_TICKET);} else {//查詢數據庫中的component_verify_ticketcomponent_verify_ticket = platformMapper.selectTicket();redisUtils.set(CacheKey.OPEN_PLATFORM_TICKET, component_verify_ticket, 60 * 60 * 12);}if (StringUtils.isBlank(component_verify_ticket)) {throw new BadRequestException("微信開放平臺,第三方平臺獲取【驗證票據】失敗");}params.put("component_verify_ticket", component_verify_ticket);JSONObject data = JSONObject.parseObject(HttpUtil.post(componentAccessTokenUrl, JSON.toJSONString(params)));if (data.containsKey("component_access_token")) {String componentAccessToken = data.getString("component_access_token");//將component_access_token存入redis中,并設置2個小時的過期時間redisUtils.set(CacheKey.COMPONENT_ACCESS_TOKEN, componentAccessToken, 60 * 60 * 2);return;}redisUtils.del(CacheKey.COMPONENT_ACCESS_TOKEN);redisUtils.del(CacheKey.OPEN_PLATFORM_TICKET);throw new BadRequestException("獲取component_access_token失敗,請重試,接口返回信息:" + data.toJSONString()); }

3、獲取authorizer_access_token

步驟:

獲取預授權碼pre_auth_code——>微信公眾平臺管理員授權——>獲取authorizer_access_token&authorizer_refresh_token

官方文檔:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/Before_Develop/Authorization_Process_Technical_Description.html

①獲取預授權碼pre_auth_code

@Override public String getPreAuthCode() {//先判斷component_access_token是否過期,如果過期重新獲取getComponentAccessToken();String preAuthCodeUrl = "https://api.weixin.qq.com/cgi-bin/component/api_create_preauthcode?component_access_token=" + redisUtils.get(CacheKey.COMPONENT_ACCESS_TOKEN);JSONObject params = new JSONObject();params.put("component_appid", APPID);JSONObject data = JSONObject.parseObject(HttpUtil.post(preAuthCodeUrl, JSON.toJSONString(params)));if (data.containsKey("pre_auth_code")) {return data.getString("pre_auth_code");}throw new BadRequestException("獲取預授權失敗,接口返回信息:" + data.toJSONString()); }

②微信公眾平臺管理員授權

生成授權鏈接,可以在前端用a標簽填入這個接口地址,點擊a標簽打開新標簽頁,管理員進行掃碼授權

@Override public void toAuthorization(HttpServletResponse response) throws IOException {String authUrl = "https://mp.weixin.qq.com/cgi-bin/componentloginpagecomponent_appid=%s&pre_auth_code=%s&redirect_uri=%s&auth_type=1";String redirectUrl = "https://服務端地址/callback";String preAuthCode = getPreAuthCode();response.setStatus(301);response.sendRedirect(String.format(authUrl, APPID, preAuthCode, redirectUrl)); }

授權之后,會回調:“https://服務端地址/callback”,并將授權碼auth_code攜帶到這個接口

@ApiOperation(value = "微信開放平臺:用戶授權后,回調地址", notes = "zhuguangliang")@AnonymousGetMapping("/callback")@Notice("2393194918@qq.com")public void callback(String auth_code) { // openPlatformService.getAuthorizationInfo(auth_code);}

③獲取authorizer_access_token&authorizer_refresh_token

官方文檔:https://developers.weixin.qq.com/doc/oplatform/Third-party_Platforms/2.0/api/ThirdParty/token/authorization_info.html

說明:authorization_code就是授權之后,返回的授權碼

如果你使用微信的第三方平臺管理工具,可以在數據庫中直接獲取authorizer_refresh_token,然后根據:

獲取/刷新接口調用令牌 | 微信開放文檔 (qq.com)這個接口進行刷新authorizer_access_token,下面是實現代碼:

@Override//authorizerAppId為授權的appid public void getAuthorizationInfo(String authorizerAppId) {String key = CacheKey.AUTH_ACCESS_TOKEN + authorizerAppId;//這一步是為如果發現這個token快過期,則刷新if (redisUtils.hasKey(key) && redisUtils.getExpire(key) > 5 * 60) {return;}getComponentAccessToken();String refreshTokenUrl = "https://api.weixin.qq.com/cgi-bin/component/api_authorizer_token?component_access_token=" + redisUtils.get(CacheKey.COMPONENT_ACCESS_TOKEN);JSONObject param = new JSONObject();param.put("component_appid", APPID);param.put("authorizer_appid", authorizerAppId);String refreshToken;if (redisUtils.hasKey(CacheKey.AUTH_REFRESH_TOKEN + authorizerAppId)) {refreshToken = (String) redisUtils.get(CacheKey.AUTH_REFRESH_TOKEN + authorizerAppId);} else {//從微信第三方管理工具的數據庫中查詢出authorizer_refresh_tokenrefreshToken = platformMapper.selectRefreshTokenByAppId(authorizerAppId);if (StringUtils.isBlank(refreshToken)) {throw new BadRequestException("小程序管理員未授權,請讓小程序管理員重新授權");}//計算刷新令牌的過期時間//從微信第三方管理工具的數據庫中查詢出authorizer_refresh_token的過期時間LocalDateTime authTime = platformMapper.selectAuthTimeByAppId(authorizerAppId).toLocalDateTime();Duration duration = Duration.between(authTime, LocalDateTime.now());redisUtils.set(CacheKey.AUTH_REFRESH_TOKEN + authorizerAppId, refreshToken, 60 * 60 * 24 * 30 - (duration.toMillis() / 1000 + 60));}param.put("authorizer_refresh_token", refreshToken);JSONObject tokenData = JSONObject.parseObject(HttpUtil.post(refreshTokenUrl, JSON.toJSONString(param)));if (tokenData.containsKey("authorizer_access_token")) {redisUtils.set(CacheKey.AUTH_ACCESS_TOKEN + authorizerAppId, tokenData.getString("authorizer_access_token"), 2 * 60 * 60);LambdaUpdateWrapper<SpiritOpenPlatform> updateWrapper = new LambdaUpdateWrapper<>();updateWrapper.eq(SpiritOpenPlatform::getAppid, authorizerAppId);SpiritOpenPlatform openPlatform = new SpiritOpenPlatform();openPlatform.setAuthorizerRefreshToken(refreshToken);openPlatform.setAuthorizerAccessToken(tokenData.getString("authorizer_access_token"));platformMapper.update(openPlatform, updateWrapper);return;}throw new BadRequestException("獲取authorizer_access_token失敗,接口返回信息:" + tokenData.toJSONString()); }

到此,微信第三方平臺開發中的關鍵token,authorizer_access_token已經成功獲取

4、調接口發布小程序

提交代碼接口:上傳小程序代碼并生成體驗版 | 微信開放文檔 (qq.com)

設置用戶隱私接口:配置小程序用戶隱私保護指引 | 微信開放文檔 (qq.com)

提交審核接口:提交審核 | 微信開放文檔 (qq.com)

查詢審核結果接口:查詢最新一次提交的審核狀態 | 微信開放文檔 (qq.com)

  • 項目中可以采用定時任務的策略調用該接口

最后

總結:微信第三方平臺開發的流程,涉及的接口有點多,微信的文檔其實也寫的很詳細了,但是在開發過程中,免不了遇見莫名其妙的問題,這個時候可以在微信開放社區里找找或者搜下博客。

總結

以上是生活随笔為你收集整理的微信第三方平台开发的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。