公众平台第三方平台是为了让公众号或小程序运营者,在面向垂直行业需求时,可以一键授权给第三方平台(并且可以同时授权给多家第三方),通过第三方平台来完成业务,开放给所有通过开发者资质认证后的开发者使用。

通过第三方平台,我们可以替用户代为管理小程序,包括快速创建、代码发布、小程序基础信息设置等等一系列的功能,都可以通过微信第三方平台实现。

准备工作:

首先需要申请创建第三方服务平台账号,填写好对应的资料信息。

以上的开发资料信息都是申请时需要我们自己填写的。

授权发起页域名:公众号和小程序发起授权的页面操作,必须在该域名下发起
测试公众号列表:全网发布以前,只有列表里的公众号才能继续授权,这里是小程序和公众号的原始id

授权时间接收URL:该回调接口主要用来接收微信官方推送的消息通知,用户授权、取消授权、发送ticket等都通过这个接口,必须返回success响应
消息校验Token:收到微信的消息时,解密校验用
消息加密Key:微信和我们通信时,消息加解密密钥
消息与事件接收URL:用来接收公众号和小程序发送的消息,小程序代码审核的结果通知
小程序服务器域名:小程序服务器域名地址,用于小程序发起网络请求
小程序业务域名:小程序调用web-view接口,打开的网页必须在此域名下
白名单ip地址列表:仅此ip名单下才可以调用微信的接口

在进行授权之前,首先我们要先获取到component_access_token,就是得先把咱们自己第三方服务平台的票据啥的先拿到。

1、推送component_verify_ticket协议

在第三方平台创建审核通过后,微信服务器会向其“授权事件接收URL”每隔10分钟定时推送component_verify_ticket。第三方平台方在收到ticket推送后也需进行解密,接收到后必须直接返回字符串success。

POST数据示例

<xml>
<AppId> </AppId>
<CreateTime>1413192605 </CreateTime>
<InfoType> </InfoType>
<ComponentVerifyTicket> </ComponentVerifyTicket>
</xml>

字段说明

字段名称                                   字段描述
AppId                                        第三方平台appid
CreateTime                               时间戳
InfoType                                   component_verify_ticket
ComponentVerifyTicket            Ticket内容
代码:

静态类用于保存获取的ticket,和token信息,实际开发中最好是保存在数据库里

public class OpenComponentParams {public static String aesKey = "0JpPmBUBCe7EFeEg9EDcEklWYGXFNLg6123asdafs"; //aeskeypublic static String COMPONENT_TOKEN = "open-mini-token"; //平台设定的tokenpublic static String appId = "wx59bfe3a871afadega1";public static String secret = "1de8bc72c6c21e8asdagfsfa12323";public static String pre_auth_code;public static String TICKET = "";// 第三方ticketpublic static String ACCESS_TOKEN = "";// token}
接收ticket方法,注意接口url为我们申请时填写的授权时间接收URL@RestController
public class TicketController {/*** 获得授权事件的票据.** @param timestamp 时间戳* @param nonce 随机数* @param encryptType 加密类型 aes* @param msgSignature 消息体签名* @param postdata 消息体* @return 如果获得只需要返回 SUCCESS*/@RequestMapping("/ticket")public String ticket(String timestamp, String nonce,@RequestParam("encrypt_type") String encryptType,@RequestParam("msg_signature") String msgSignature, @RequestBody String postdata) {try {//这个类是微信官网提供的解密类,需要用到消息校验Token 消息加密Key和服务平台appidWXBizMsgCrypt pc = new WXBizMsgCrypt(OpenComponentParams.COMPONENT_TOKEN,OpenComponentParams.aesKey, OpenComponentParams.appId);String xml = pc.decryptMsg(msgSignature, timestamp, nonce, postdata);Map<String, String> result = Xml2MapUtil.dom2Map(xml);// 将xml转为mapString componentVerifyTicket = MapUtils.getString(result, "ComponentVerifyTicket");// 存储平台授权票据,保存ticketOpenComponentParams.TICKET = componentVerifyTicket;} catch (Exception e) {log.error(e.getMessage(), e);e.printStackTrace();}return "success";}
}

附官方示例解密方法,做了些小改动,官网提供的extact方法会报空指针...

/*** 对公众平台发送给公众账号的消息加解密示例代码.* * @copyright Copyright (c) 1998-2014 Tencent Inc.*/// ------------------------------------------------------------------------/*** 针对org.apache.commons.codec.binary.Base64, 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)* 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi*/
package com.linshang.auth.common.aes;import java.io.StringReader;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Random;
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 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;/*** 提供接收和推送给公众平台消息的加解密接口(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.ValidateAppidError);}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);}}
}

2、获取第三方平台component_access_token

第三方平台component_access_token是第三方平台的下文中接口的调用凭据,也叫做令牌(component_access_token)。每个令牌是存在有效期(2小时)的,且令牌的调用不是无限制的,请第三方平台做好令牌的管理,在令牌快过期时(比如1小时50分)再进行刷新。

接口调用请求说明

http请求方式: POST(请使用https协议) 
https://api.weixin.qq.com/cgi-bin/component/api_component_token
POST数据示例:

{
"component_appid":"appid_value" ,
"component_appsecret": "appsecret_value",
"component_verify_ticket": "ticket_value"
}
请求参数说明

参数                                   说明
component_appid              第三方平台appid
component_appsecret       第三方平台appsecret
component_verify_ticket    微信后台推送的ticket,此ticket会定时推送,具体请见本页的推送说明

返回结果示例

{"component_access_token":"61W3mEpU66027wgNZ_MhGHNQDHnFATkDa9-2llqrMBjUwxRSNPbVsMmyD-yq8wZETSoE5NQgecigDrSHkPtIYA", "expires_in":7200}

结果参数说明

参数                                       说明
component_access_token    第三方平台access_token
expires_in                              有效期
代码示例:

因为token两小时就会过期,所以我这里写了一个定时器,定期刷新token

@Service
public class TimeScheduler {@Resourceprivate WeiXinApi weiXinApi;@Scheduled(cron = "0 0 0/1 * * ?")public void timeTask3() {/* 核心定时器,每一个小时执行一次*/// 先看看是不是已经获取到了ticketif (StringUtils.isNotBlank(OpenComponentParams.TICKET)) {Map<String, Object> params = new HashMap<>();params.put("component_appid", OpenComponentParams.appId);params.put("component_appsecret", OpenComponentParams.secret);params.put("component_verify_ticket", OpenComponentParams.TICKET);//微信接口客户端Map<String, Object> result = weiXinApi.componentToken(params);//保存获取的tokenOpenComponentParams.ACCESS_TOKEN = MapUtils.getString(result, "component_access_token");}}}
}

使用FeignClient调用微信接口:

@FeignClient(name = "weixinapi", url = "https://api.weixin.qq.com")
public interface WeiXinApi {/*** 获取第三方平台component_access_token.* * @param params 发送的参数* @return 请求结果*/@PostMapping("/cgi-bin/component/api_component_token")Map<String, Object> componentToken(Map<String, Object> params);/*** 获取预授权码pre_auth_code.* * @param componentAccessToken 请求token* @param params 第三方平台方appid* @return 预授权码pre_auth_code*/@PostMapping(value = "/cgi-bin/component/api_create_preauthcode")Map<String, Object> preauthcode(@RequestParam("component_access_token") String componentAccessToken,Map<String, Object> params);/*** 使用授权码换取公众号或小程序的接口调用凭据和授权信息.* * @param componentAccessToken 平台可访问授权码* @param params post的参数* @return api执行结果*/@PostMapping("/cgi-bin/component/api_query_auth")Map<String, Object> apiQueryAuth(@RequestParam("component_access_token") String componentAccessToken,Map<String, Object> params);}

微信第三方服务平台java授权获取token(一)相关推荐

  1. 微信平台第三方服务器,浅谈微信第三方服务平台的发展前景

    伴随着移动端的普及,移动互联网也在飞速的发展,同时也有越来越多的小商家开始抓住了移动互联网的这个渠道开展营销活动.而随着微信的不断更新和发展,微信用户也越来越多,而如今如雨后春笋般出现的各类微信第三方 ...

  2. 微信第三方服务平台开发(一)

    工作中遇到开发微信第三方服务的需求,学习的同时记录一下开发的过程,给大家提供一个参考,希望能有些帮助,让大家少走些弯路. 首先说下开发流程,微信公众平台提供了大量的第三方接口供开发者调用,可以丰富微信 ...

  3. 微信第三方服务平台源码分析——每个Action与模块的对应关系

    Lib\Action    |__Home--------前台页面中的首页,功能介绍,关于我们,帮助中心    |__System------后台管理       |__AdminAction     ...

  4. 微信开发(4):微信第三方开放平台的搭建(java)

    什么是第三方开放平台 来波官方解释: 我才是官方文档 第三方平台的开放,让公众号或小程序运营者在面向垂直行业需求时,可以通过一键登录授权给第三方开发者,来完成相关能力. 简单的说,就是让公众号授权给第 ...

  5. 更新一波,微信第三方开发平台授权流程

    最近一直忙于微信三方开发平台开发,更新一下,做个记录,微信第三方开发平台授权流程示例: 先看授权流程要拿到的结果: 照例先给出微信授权流程官网页面:https://open.weixin.qq.com ...

  6. 微信公共服务平台开发(.Net 的实现)13-------网页授权(下 :C#代码的实现 )

    接着上次的理论,我们这次来研究用代码实现"网页授权获取用户基本信息",首先我们需要一个链接指向微信的授权页面,在微信开发平台中已经说了,这个链接必须在微信客户端中打开,那么我们就干 ...

  7. 微信公众帐号第三方服务平台源码

    微信公众帐号第三方服务平台源码,基于thinkphp3.2版本+DWZ进行开发.下载地址:http://down.qypangu.com 现在这个系统只能算是一个DEMO版,各位下载下来后可以根据自己 ...

  8. php把微信一键登录,PHP微信第三方实现一键登录及获取用户信息的方法(实例详解)...

    这篇文章主要介绍了PHP版微信第三方实现一键登录及获取用户信息的方法,较为详细的分析了微信第三方登陆的相关注意事项与实现技巧,需要的朋友可以参考下 注意,要使用微信在第三方网页登录是需要"服 ...

  9. 电商平台-Java后端生成Token架构与设计详解

    目的:Java开源生鲜电商平台-Java后端生成Token目的是为了用于校验客户端,防止重复提交. 技术选型:用开源的JWT架构. 1.概述:在web项目中,服务端和前端经常需要交互数据,有的时候由于 ...

最新文章

  1. 抗击疫情,AI一直在行动
  2. 关于js中的时间处理
  3. android java 给控件设置style,在Android Lollipop for Material Design中为SwitchCompat按钮设置样式/着色...
  4. 不同dll相同Type.FullName引发的问题
  5. 武汉游记,三件新鲜奇葩事
  6. WindowsSdkDir 从何处来?
  7. struts2文件上传,下载
  8. linux什么命令查设备型号,在Linux命令行中查看系统硬件制造商、型号与序列号的六种方法...
  9. java io装饰类,Java IO 装饰类新说
  10. 【渝粤教育】电大中专金融与税收 (2)_1作业 题库
  11. 人工智能实验2——用遗传算法求解TSP问题
  12. Linux命令大全.pdf
  13. 盘点:12个超炫数据可视化工具
  14. 如何将php网页打印成pdf,新技能!如何把网页打印成pdf文件?
  15. 机器学习笔记 - 什么是高斯混合模型(GMM)?
  16. 2000 Followers-3D CSS text
  17. 论“蹭热点”,我只服“杜蕾斯”,今天咋们一起来盘点 它曾蹭过的10大神热点!...
  18. 电脑android模拟器下载地址,菜鸡电脑版怎么下载 安卓模拟器电脑版下载地址
  19. 2012春节回乡见闻
  20. 自举电路工作原理和自举电阻和电容的选取

热门文章

  1. excel查找在哪里_HR:对不起,我们公司不招25岁还用不好Excel的人
  2. 网络复现之基于TPS的STN网络
  3. 怎么用单片机做一个比较有创新的东西?
  4. Spark Streaming与流处理
  5. 获取颜色值 抓取颜色值 获取颜色代码RGB
  6. R语言下载GEOquery包
  7. 人脸识别之人脸对齐(三)--AAM算法
  8. LaTex单词的间隔
  9. 开源框架XWIKI搭建介绍
  10. 如何制作小游戏(c++教程)(新手版)(1)