一、构建spring boot项目
1、新建项目
新建一个模块(module):enterprise-wechat
新建一个子模块(module):wechat
目录结构如下:

文章插图
结构描述:
common
-> WeChatConstants:存放企业微信一些常量,公用参数
-> WeChatUtils:存放企业微信第三方应用api
controller
-> SystemController:控制层,接收请求
entity
-> aes:目录下文件企业微信加解密包
service
-> IConfigService:调用企业微信服务层
pom.xml
-> 导入所需要的jar包
pom.xml中需要导入commons.codec包
<dependency><groupId>commons-codec</groupId><artifactId>commons-codec</artifactId><version>1.9</version></dependency>2、方法描述1)doGetCallback:
① 接收验证请求,用于验证通用开发参数系统事件接收URL、数据回调URL、指令回调URL 。
② 企业微信后台录入回调URL点击保存时,微信服务器会立即发送一条GET请求到对应URL,该函数就对URL的signature进行验证 。
2)doPostCallback:
① 用于获取 suite_ticket,安装应用时企业微信传递过来的auth_code:指令回调URL 。
② 当刷新ticket传递【SuitID】:指令回调URL 。
③ 当打开应用时传递【CorpID】:数据回调URL 。
3、代码编写
1)企业微信配置类:WeChatConstants
package com.wechat.common;/** * 企业微信 */public class WeChatConstants {// 企业微信授权码获取时间public static final Long EXPIRES_IN = 24 * 60 * 60 * 1000L;//24 * 60 * 60 * 1000L 7200L * 1000/*** 服务商CorpID*/public static final String CORP_ID = "ww14438c6c07a317f2";/*** 服务商身份的调用凭证*/public static final String PROVIDER_SECRET = "RH7PehRJX3LIcw4axad_H2T9HSUG1finOBEpnLTVIioBrP-zgZrGsqJ9pHVw5vVj";/*** 应用的唯一身份标识*/public static final String SUITE_ID = "ww4f66fa544a32f920";/*** 应用的调用身份密钥*/public static final String SUITE_SECRET = "vVv8JzaBlEVCTQkHKqmr57EAMs65AILWiI_4ANc25T4";// 回调相关/*** 回调/通用开发参数Token, 两者解密算法一样,所以为方便设为一样*/public static final String TOKENS = "E0sOXx4LqeE5BmDvMTAz3x";/*** 回调/通用开发参数EncodingAESKey, 两者解密算法一样,所以为方便设为一样*/public static final String ENCODING_AES_KEY = "IESLPSyW4vyBB90jkzfwfYRtcMky6LIOevr4SVefz7I";}【企业微信第三方应用开发 三 企业微信第三方应用基于springboot开发(获取Ticket,auth_code)】2)企业微信api:WeChatUtilspackage com.wechat.common;/** * 企业微信工具类 */public class WeChatUtils {/*** 第三方应用api start*/// 获取第三方应用凭证public final static String THIRD_BUS_WECHAT_SUITE_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token";// 获取企业永久授权码public final static String THIRD_BUS_WECHAT_ACCESS_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=SUITE_ACCESS_TOKEN";// 第三方 构造扫码登录链接public final static String THIRD_BUS_WECHAT_LOGIN = "https://open.work.weixin.qq.com/wwopen/sso/3rd_qrConnect?appid=CORPID&redirect_uri=REDIRECT_URI&state=web_login&usertype=member";// 第三方 获取登录用户信息 POSTpublic final static String THIRD_BUS_WECHAT_GET_LOGIN_INFO = "https://qyapi.weixin.qq.com/cgi-bin/service/get_login_info?access_token=PROVIDER_ACCESS_TOKEN";// 第三方 构造网页授权链接public final static String THIRD_BUS_WECHAT_AUTHORIZE_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_privateinfo&state=STATE#wechat_redirect";// 第三方 获取访问用户身份 GETpublic final static String THIRD_BUS_WECHAT_GET_USER_INFO = "https://qyapi.weixin.qq.com/cgi-bin/service/getuserinfo3rd?suite_access_token=SUITE_TOKEN&code=CODE";// 第三方 获取访问用户敏感信息 postpublic final static String THIRD_BUS_WECHAT_GET_USER_DETAIL3RD = "https://qyapi.weixin.qq.com/cgi-bin/service/getuserdetail3rd?suite_access_token=SUITE_ACCESS_TOKEN";// 第三方 获取部门列表public final static String THIRD_BUS_WECHAT_DEPART_LIST = "https://qyapi.weixin.qq.com/cgi-bin/department/list?access_token=ACCESS_TOKEN&id=ID";// 第三方 获取部门成员public final static String THIRD_BUS_WECHAT_DEPART_USER = "https://qyapi.weixin.qq.com/cgi-bin/user/simplelist?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID&fetch_child=FETCH_CHILD";// 第三方 获取部门成员详情public final static String THIRD_BUS_WECHAT_DEPART_USER_DETAIL = "https://qyapi.weixin.qq.com/cgi-bin/user/list?access_token=ACCESS_TOKEN&department_id=DEPARTMENT_ID&fetch_child=FETCH_CHILD";// 第三方 读取成员 GETpublic final static String THIRD_BUS_WECHAT_GET_USER = "https://qyapi.weixin.qq.com/cgi-bin/user/get?access_token=ACCESS_TOKEN&userid=USERID";// 服务商的tokenpublic final static String THIRD_BUS_WECHAT_GET_PROVIDER_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/service/get_provider_token";// 获取企业凭证public final static String THIRD_BUS_WECHAT_GET_CORP_TOKEN = "https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=SUITE_ACCESS_TOKEN";// 发送应用消息public final static String THIRD_BUS_WECHAT_SEND = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN";// 获取应用的jsapi_ticketpublic final static String THIRD_BUS_GET_JSAPI_TICKET = "https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=ACCESS_TOKEN&type=agent_config";// 获取企业的jsapi_ticketpublic final static String THIRD_BUS_GET_JSAPI_TICKET_BUS = "https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKEN";/*** 第三方应用api end*/}3)controller层:SystemControllerpackage com.wechat.controller;import com.wechat.service.IConfigService;import io.swagger.annotations.ApiOperation;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.ServletInputStream;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.PrintWriter;/** * 控制层 */@Slf4j@RestController@RequestMapping(value = "https://tazarkount.com/read/system")public class SystemController {@Autowiredprivate IConfigService configService;/*** 验证通用开发参数及应用回调* @param: request* @param: response* @returns: void*/@ApiOperation(value = "https://tazarkount.com/read/验证通用开发参数及应用回调")@GetMapping(value = "https://tazarkount.com/read/getEchostr")public void doGetCallback(HttpServletRequest request, HttpServletResponse response) throws Exception {// 微信加密签名String msgSignature = request.getParameter("msg_signature");// 时间戳String timestamp = request.getParameter("timestamp");// 随机数String nonce = request.getParameter("nonce");// 随机字符串// 如果是刷新,需返回原echostrString echoStr = request.getParameter("echostr");String sEchoStr="";PrintWriter out;log.debug("msgSignature: " + msgSignature+"timestamp="+timestamp+"nonce="+nonce+"echoStr="+echoStr);try {sEchoStr = configService.doGetCallback(msgSignature,timestamp,nonce,echoStr); //需要返回的明文;log.debug("doGetCallback-> echostr: " + sEchoStr);// 验证URL成功,将sEchoStr返回out = response.getWriter();out.print(sEchoStr);} catch (Exception e) {//验证URL失败,错误原因请查看异常e.printStackTrace();}}/*** 刷新ticket,AuthCode*/@ApiOperation(value = "https://tazarkount.com/read/刷新ticket,AuthCode")@PostMapping(value = "https://tazarkount.com/read/getEchostr")public String doPostCallback(HttpServletRequest request) throws Exception {// 微信加密签名String msgSignature = request.getParameter("msg_signature");// 时间戳String timestamp = request.getParameter("timestamp");// 随机数String nonce = request.getParameter("nonce");// 类型String type = request.getParameter("type");// 企业idString corpId = request.getParameter("corpid");ServletInputStream in = request.getInputStream();// 刷新ticket,AuthCodeString success = configService.doPostCallback(msgSignature, timestamp, nonce, type, corpId, in);return success;}}4)Service层:IConfigServicepackage com.wechat.service;import javax.servlet.ServletInputStream;/** * 企业微信第三方服务service */public interface IConfigService {/*** 验证通用开发参数及应用回调* @returns: java.lang.String*/String doGetCallback(String msgSignature, String timestamp, String nonce, String echoStr);/*** 获取SuiteTicket,AuthCode*/String doPostCallback(String msgSignature, String timestamp, String nonce, String type, String corpId, ServletInputStream in);}5)service实现类:ConfigServiceImplpackage com.wechat.service.impl;import com.alibaba.druid.support.json.JSONUtils;import com.wechat.common.StringUtils;import com.wechat.common.WeChatConstants;import com.wechat.common.WxUtil;import com.wechat.entity.aes.AesException;import com.wechat.entity.aes.WXBizMsgCrypt;import com.wechat.service.IConfigService;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Service;import javax.servlet.ServletInputStream;import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Map;/** * TODO 类描述 */@Slf4j@Servicepublic class ConfigServiceImpl implements IConfigService {/*** 验证通用开发参数及应用回调* @returns: java.lang.String*/@Overridepublic String doGetCallback(String msgSignature, String timestamp, String nonce, String echoStr) {//需要返回的明文String sEchoStr="";try {log.debug(WeChatConstants.TOKENS, WeChatConstants.ENCODING_AES_KEY, WeChatConstants.CORP_ID);WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(WeChatConstants.TOKENS, WeChatConstants.ENCODING_AES_KEY, WeChatConstants.CORP_ID);sEchoStr = wxcpt.VerifyURL(msgSignature, timestamp, nonce, echoStr);} catch (AesException e) {e.printStackTrace();}return sEchoStr;}/*** 获取SuiteTicket,AuthCode* @param: msgSignature 微信加密签名* @param: timestamp 时间戳* @param: nonce随机数* @param: type 类型* @param: corpId 企业id* @param: in* @returns: java.lang.String*/@Overridepublic String doPostCallback(String msgSignature, String timestamp, String nonce, String type, String corpId, ServletInputStream in) {String id = "";// 访问应用和企业回调传不同的IDif(!StringUtils.isNull(type) && type.equals("data")){id = corpId;log.debug("======corpId==="+id);} else {id = WeChatConstants.SUITE_ID;log.debug("======SuiteId===" + id);}try {WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(WeChatConstants.TOKENS, WeChatConstants.ENCODING_AES_KEY, id);String postData="";// 密文,对应POST请求的数据//1.获取加密的请求消息:使用输入流获得加密请求消息postDataBufferedReader reader = new BufferedReader(new InputStreamReader(in));String tempStr = "";//作为输出字符串的临时串,用于判断是否读取完毕while(null != (tempStr=reader.readLine())){postData+=tempStr;}log.debug("====msg_signature===="+msgSignature+"====timestamp==="+timestamp+"====nonce==="+nonce+"====postDatahttps://tazarkount.com/read/==="+postData);String suiteXml = wxcpt.DecryptMsg(msgSignature, timestamp, nonce, postData);log.debug("suiteXml: " + suiteXml);Map suiteMap = WxUtil.parseXml(suiteXml);log.debug("==suiteMap=="+ JSONUtils.toJSONString(suiteMap));if(suiteMap.get("SuiteTicket") != null) {String suiteTicket = (String) suiteMap.get("SuiteTicket");log.debug("====SuiteTicket=====" + suiteTicket);} else if(suiteMap.get("AuthCode") != null){String authCode = (String) suiteMap.get("AuthCode");log.debug("doPostValid->AuthCode:" + authCode);}} catch (Exception e) {e.printStackTrace();}return "success";}}4、验证以上代码编写完成后,就可以打包到环境上面进行测试验证:
①:echostr验证

文章插图

文章插图
返回结果:返回 echostr,并显示已验证
16:11:46.940 [http-nio-9205-exec-7] INFOc.q.w.s.c.SystemController - [doGetValid,94] - doGetCallback->echostr: 57711593423634425916:11:46.969 [http-nio-9205-exec-3] INFOc.q.w.s.c.SystemController - [doGetValid,94] - doGetCallback->echostr: 5267604771365158379②:刷新Ticket:获取Ticket有两种方式,一是点击按钮获取,二是企业微信每15分钟会调用回调接口获取一次
文章插图
点击“刷新Ticket” 会弹出如下图,然后点击确定

文章插图
Ticket 有效期为30分钟;建议把Ticket放到数据库或者redis中

文章插图
③:获取auth_code
安装第三方应用的时候,会获取auth_code

文章插图
④:安装测试流程

文章插图

文章插图
通过企业微信扫码进行安装

文章插图

文章插图
上面就是验证通过,及获取Ticket和auth_code
5、总结
在第三方应用开发中,主要围绕三种类型的access_token(见企业微信地方应用(二)https://www.cnblogs.com/why0703/p/15983925.html)
provider_access_token:服务商的token
suite_access_token:获取第三方应用凭证
access_token:授权方(企业)access_token
通过上面的代码及配置,我们获取到了suiteTicket和auth_code 。
接下来我们要通过这些值获取到上面token,通过springboot开发实现“企业微信第三方应用(二)api使用测试”
代码后面同步到github
- 春季老年人吃什么养肝?土豆、米饭换着吃
- 三八妇女节节日祝福分享 三八妇女节节日语录
- 老人谨慎!选好你的“第三只脚”
- 校方进行了深刻的反思 青岛一大学生坠亡校方整改校规
- 脸皮厚的人长寿!有这特征的老人最长寿
- 长寿秘诀:记住这10大妙招 100%增寿
- 春季老年人心血管病高发 3条保命要诀
- 眼睛花不花要看四十八 老年人怎样延缓老花眼
- 香槟然能防治老年痴呆症? 一天三杯它人到90不痴呆
- 老人手抖的原因 为什么老人手会抖
