一、首先要了解开发场景,第三方应用开发、企业内部开发、智慧硬件开发的区别。

企业微信对应有三个开发文档,要注意三个开发文档虽然说部分接口是通用的,但是其接口获取的内容、调用的本质却大有不同,我建议先把企业微信开发者前言部分的细读,搞明白了三者的概念。此处我均已第三方应用开发为准(申请部分的内容网上教程一大堆,大家跟着步骤走等审批就行)

二、服务商后台 - 应用管理 - 小程序 - 配置回调


虽然说企业微信需要配置的url很多,但是最主要的还是回调接口,这也是接入企业微信的第一步。这个回调接口,每10分钟会接收到企业微信发出的请求,刷新当前suiteTicket,然后我们需要在GET请求中校验企业微信的参数,然后在post接口中对接收到的参数进行解密,并且需要将suiteTicket转换为suiteAccessToken,作为请求企业微信接口的第一个重要凭证,GET、POST回调的Java代码示范:

GET回调接口(企业微信工具类代码自行下载官方demo即可)

@GetMapping("/callback")
@ResponseBody
public void callback(@RequestParam(name = "msg_signature") String signature, String timestamp, String nonce,String echostr, final HttpServletResponse response) {log.info("get验签请求参数 msg_signature = {}, timestamp = {}, nonce = {} , echostr = {}", signature, timestamp, nonce, echostr);wxMpservice.getCallback(signature, timestamp, nonce, echostr, response);
}@Override
public void getCallback(String signature, String timestamp, String nonce, String echostr, HttpServletResponse response) {WXBizMsgCrypt wxcpt = null;String sEchoStr = null;try {wxcpt = new WXBizMsgCrypt(token, encodingAESKey, corpid);sEchoStr = wxcpt.VerifyURL(signature, timestamp, nonce, echostr);} catch (AesException e) {e.printStackTrace();}log.info("weixin-callback:get请求回调签名校验通过, result = " + sEchoStr);PrintWriter out = null;try {out = response.getWriter();//必须要返回解密之后的明文if (GeneralUtil.isNotNullAndEmpty(sEchoStr)) {log.info("验证成功!");} else {log.error("URL验证失败");}} catch (Exception e) {e.printStackTrace();}out.write(sEchoStr);out.flush();
}

POST接口(注意此处需要对suiteTicket做缓存处理,并且需要用suiteTicket转为suiteAccessToken。此外!倘若有事件回调,必须要区分微信回调和第三方服务商的回调,否则POST获取到的解密内容是不同的!)

/*** 企业微信客户联系回调.** @param request       request* @param sMsgSignature 签名* @param sTimestamp    时间戳* @param sNonce        随机值* @return success*/
@ResponseBody
@PostMapping(value = "/callback")
public String callback(final HttpServletRequest request,@RequestParam(name = "msg_signature") final String sMsgSignature,@RequestParam(name = "timestamp") final String sTimestamp,@RequestParam(name = "nonce") final String sNonce) {log.info("post验签请求参数 msg_signature = {}, timestamp = {}, nonce {}", sMsgSignature, sTimestamp, sNonce);wxMpservice.postCallback(request, sMsgSignature, sTimestamp, sNonce);return "success";
}@Override
public void postCallback(HttpServletRequest request, String sMsgSignature, String sTimestamp, String sNonce) {WXBizMsgCrypt wxcpt = null;try {InputStream inputStream = request.getInputStream();String postData = CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));wxcpt = new WXBizMsgCrypt(token, encodingAESKey, corpid);// 解密String sMsg = wxcpt.DecryptMsg(sMsgSignature, sTimestamp, sNonce, postData);// 将post数据转换为mapMap<String, String> dataMap = MessageUtil.parseXml(sMsg);log.warn("回调map = {}", dataMap);// dataMap需要判断, 如果是事件回调, 则无需设置tokenif (Objects.nonNull(dataMap.get("SuiteTicket")) && Objects.nonNull(dataMap.get("SuiteId"))) {log.info("weixin-callback:post请求回调成功, dataMap = " + dataMap);// suite_ticket每10分钟刷新一次, 每个suite_ticket有效期为30分钟, redis会根据key直接覆盖旧值redisTemplate.opsForValue().set(RedisKeyConstants.WX_POST_CALLBACK_SUITE_TICKET, dataMap, 20L, TimeUnit.MINUTES);} else {//企业内成员idString openUserId = dataMap.get("FromUserName");// 事件需要 template_card_eventString event = dataMap.get("Event");// EventKey id需要对应上  button_key_1 button_key_2 目前设置两个按钮一个为通过 一个为不通过String eventKey = dataMap.get("EventKey");String responseCode = dataMap.get("ResponseCode");String taskId = dataMap.get("TaskId");if (GeneralUtil.isNotNullAndEmpty(responseCode) && GeneralUtil.isNotNullAndEmpty(event)) {// 更新模板按钮log.info("操作人ID:{} , 发起事件:{} , 触发按钮:{}", openUserId, event, eventKey);updateTemplateBtnMsg(responseCode);// 接受事件回调, 处理业务逻辑switch (eventKey) {// 通过case "accept_user_key":govtGroupMemberService.approveGroupEntry(taskId, Boolean.TRUE);log.info("通过通过 event = {},taskId = {}", event, taskId);break;// 拒绝case "reject_user_key":govtGroupMemberService.approveGroupEntry(taskId, Boolean.TRUE);log.info("拒绝拒绝 event = {},taskId = {}", event, taskId);break;default:break;}}}} catch (Exception e) {e.printStackTrace();}
}

代码开发完成,需要将其请求路径配置到企业微信服务商的回调url中,配置完点击确定的时候,企业微信会立马发出两条请求消息校验请求是否成功(此外,记得配置企业微信的ip白名单)

再点击刷新ticket,再次校验是否配置成功,请求POST接口

注意:我这里特意打印了from_receiveid 和 receiveid,这是为什么?因为仔细的同学会发现,当我们获取企业微信回调的时候,我们post请求解密的时候用的是suiteid,但如果是事件回调的时候,此时post请求解密用的是corpid。这里我踩了不少坑,官方给的DEMO十分之灵异,这里竟然没有特意注释和说明

// 解密是指POST-CALLBACK的这一步
wxcpt = new WXBizMsgCrypt(token, encodingAESKey, corpid);/*** 备注:在企业内部的工具类中有!from_corpid.equals(corpId)的校验* 但是在第三方应用的时候,由于postDate解密得到的是安装该应用的fromCorpID,所以不能进行比较*/
// receiveid不相同的情况,为了解决corpid权限不足的报错,可以直接注释掉这段代码
// if (!from_receiveid.equals(receiveid)) {//  throw new AesException(AesException.ValidateCorpidError);
// }

三、第三方应用授权流程开发

重新回到开始,不要觉得配置回调就OK了,然后立马去开发对接文档。我们需要了解一下,第三方应用它的角色定义,是以第三方服务商的身份,将小程序应用关联到其它企业,所以企业微信早在这一步就在开发者API上的文档做了手脚,对授权操作交给了开发者进行处理。因此,第三方服务商必须要提供一个授权的入口,并且授权成功后对回调内容进行处理。建议开发者在授权成功后,拿到回调内容进行ORM处理,因为第三方应用调用接口API的时候需要带上accessToken,并且是要通过no corpsecret的方式获取的,所以要用get_corp_token的方式获取企业永久凭证

get_corp_token的企业微信官方文档

此处授权,其实主要是两个接口,一个是创建授权链接的接口,一个是请求授权连接成功授权后的回调接口(注意,回调接口需要到服务商后台配置回调域名 and 接口地址)

此处是获取授权地址接口,因为大部分的suiteAccessToken、preAuthCode、代码官网都有,大家大概看个流程就行,最终需要接口返回一个拼凑的url。然后url提供给企业管理员扫码,确定后就能完成授权操作了。

/*** 获取第三方应用授权链接接口*/
@ResponseBody
@GetMapping(value = "/install")
public String install() {SuiteAccessToken suiteAccessToken = tokenConfig.getSuiteAccessToken();return tokenConfig.install(tokenConfig.getPreAuthCode(suiteAccessToken.getSuiteAccessToken()));
}/*** 获取预授权码** @param suiteAccessToken* @return*/
public PreAuthCode getPreAuthCode(String suiteAccessToken) {// 判断缓存是否存在, 存在则无须重复请求String cacheKey = (String) redisTemplate.opsForValue().get(RedisKeyConstants.WX_PRE_AUTH_CODE);if (Objects.nonNull(cacheKey)) {PreAuthCode preAuthCode = JSONObject.parseObject(cacheKey, PreAuthCode.class);// 设置授权setSessionInfo(preAuthCode.getPreAuthCode(), suiteAccessToken);return preAuthCode;}String result = null;try {result = HttpUtil.get(BaseUrlConstants.GET_PRE_AUTH_CODE.replace("SUITE_ACCESS_TOKEN", suiteAccessToken), null);log.info("getPreAuthCode result = " + result);} catch (Exception e) {e.printStackTrace();}// 请求返回if (Objects.nonNull(result)) {// 异常code抓取JSONObject jsonObject = JSONObject.parseObject(result);if (Objects.nonNull(jsonObject.getString("errcode")) && (!"0".equals(jsonObject.getString("errcode")))) {throw new BusinessException("getAppletAccessToken error,cause by:" + jsonObject.getString("errmsg:") + result);}// 解析PreAuthCode preAuthCode = JSONObject.parseObject(result, PreAuthCode.class);// 授权setSessionInfo(preAuthCode.getPreAuthCode(), suiteAccessToken);// 缓存applet_access_token, 7200s过期redisTemplate.opsForValue().set(RedisKeyConstants.WX_PRE_AUTH_CODE, JSONObject.toJSONString(preAuthCode),3600L, TimeUnit.SECONDS);return preAuthCode;} else {throw new BusinessException("preAuthCode获取失败!请重新授权!");}
}/*** 组装拼接URL*/
public String install(PreAuthCode preAuthCode) {// 拼凑urlString result = BaseUrlConstants.INSTALL_URL.replace("SUITE_ID", suiteId).replace("PRE_AUTH_CODE", preAuthCode.getPreAuthCode()).replace("REDIRECT_URI", "https://~~~服务商后台配置的授权回调地址 ~~/wx/mp/installCallback").replace("STATE", GeneralUtil.generateShortUuid());return result;
}

授权回调

/*** 第三方应用授权回调** @param authCode* @return*/
@ResponseBody
@GetMapping(value = "/installCallback")
public String installCallback(@RequestParam(name = "auth_code") String authCode) {// 接收到请求参数进行回调, 初始化企业信息SuiteAccessToken suiteAccessToken = tokenConfig.getSuiteAccessToken();tokenConfig.installCallback(authCode, suiteAccessToken.getSuiteAccessToken());return "授权成功!请关闭当前页!";
}/*** 临时authCode置换永久code, 并且初始化企业信息** @param authCode* @param suiteAccessToken*/
public void installCallback(String authCode, String suiteAccessToken) {// 获取企业永久凭证初始化到ORMJSONObject requestObj = new JSONObject();requestObj.put("auth_code", authCode);String result = null;try {result = HttpUtil.post(BaseUrlConstants.GET_PERMANENT_CODE_URL.replace("SUITE_ACCESS_TOKEN", suiteAccessToken), requestObj.toString());// 一次性请求, 授权成功初始化到数据库JSONObject jsonObject = JSONObject.parseObject(result);String permanentCode = jsonObject.getString("permanent_code");String authCorpInfo = jsonObject.getString("auth_corp_info");JSONObject authCorpObject = JSONObject.parseObject(authCorpInfo);String corpName = authCorpObject.getString("corp_name");String corpFullName = authCorpObject.getString("corp_full_name");// 获取agentId, 授权的应用idJSONObject childObject  = (JSONObject) jsonObject.get("auth_info");JSONArray jsonArray = JSONObject.parseArray(JSONObject.toJSONString(childObject.get("agent")));JSONObject arrayObject = (JSONObject) jsonArray.get(0);Long agentId = arrayObject.getLong("agentid");GovtInstallDO govtInstallDO = new GovtInstallDO();govtInstallDO.setAgentId(agentId);govtInstallDO.setCorpName(corpName);govtInstallDO.setCorpFullName(corpFullName);govtInstallDO.setCreateTime(new Date());govtInstallDO.setPermanentCode(permanentCode);govtInstallMapper.insert(govtInstallDO);log.info("installCallback result = " + result);} catch (Exception e) {throw new BusinessException(StatusCodeEnum.INSTALL_CORP_ERROR);}}

【企业微信急速救心丸】(一)第三方应用开发 - Java整合企业微信回调相关推荐

  1. 微信开放平台之第三方平台开发,模板小程序如何提交?

    大家好,我是悟空码字 12月25日,天气晴朗,阳光普照,今天是圣诞节.因为疫情影响,小羊人的增多,街上放眼望去,人烟稀少.楼下除了几个十一二岁的小男孩在玩耍,也没有像往日老人悠闲打牌.小孩嬉戏那般热闹 ...

  2. 微信开放平台【第三方平台】java开发总结:预授权码(pre_auth_code)(三)

    微信第三方平台预授权码(pre_auth_code)开发说明(全网最详细的微信第三方平台预授权码开发说明) 预授权码 预授权码(pre_auth_code)是第三方平台方实现授权托管的必备信息,每个预 ...

  3. 微信开放平台【第三方平台】java开发总结:验证票据(component_verify_ticket)(-)

    微信第三方平台验证票据(component_verify_ticket)开发说明(全网最详细的微信第三方平台验证票据开发说明) 概述 微信公众平台-第三方平台(简称第三方平台)开放给所有通过开发者资质 ...

  4. 微信第三方扫描登录 java源代码_微信开放平台基于网站应用授权登录源码(java)...

    1. 第三方发起微信授权登录请求,微信用户允许授权第三方应用后,微信会拉起应用或重定向到第三方网站,并且带上授权临时票据code参数: 2. 通过code参数加上AppID和AppSecret等,通过 ...

  5. 微信商城二次开发php,php版微信小店API二次开发及使用示例-微信开发

    本文实例讲述了php版微信小店API二次开发及使用方法.分享给大家供大家参考,具体如下: 1. weixiaodian.php页面: class wXd { public $AppID = " ...

  6. 微信开放平台之第三方平台开发,从哪里入手?

    大家好,我是悟空码字 疫情之下,最近有不少兄弟没有挺进决赛,半途成了小羊人,可谓是出师未捷身先死.话说回来,不管怎么样,尽量保护好自己,能越晚变羊越好. 开始说正事,不管是自己在创业或学习也好,还是公 ...

  7. 微信开放JS-SDK,助力网页开发[转自微信官方]

    为什么80%的码农都做不了架构师?>>>    微信公众平台今日面向开发者开放微信内网页开发工具包(微信JS-SDK). 通过微信JS-SDK提供的11类接口集,开发者不仅能够在网页 ...

  8. 微信jssdk开发java版_微信jssdk

    class JSSDK { private $appId; private $appSecret; public function __construct($appId, $appSecret) { ...

  9. 微信公众号开发java流程_微信公众号开发教程java 编程语言的特点及选择

    微信公众号开发教程java 编程语言的特点及选择 微信公众号为用户提供了相关的工具,来对微信公众号进行一个简单的开发.但是如果想实现一些复杂的功能,其实还是要借助于一些编程语言的使用.所以要了解,在微 ...

最新文章

  1. linux怎么改程序图标,如何在Ubuntu Unity上更换应用程序图标
  2. oracle12数据库安装步骤,ORACLE RAC 12C(12.2.0.1)数据库软件安装步骤
  3. python词云图代码示例 无jieba_【词云图】如何用python的第三方库jieba和wordcloud画词云图...
  4. 页面编码和被请求的资源编码如果不一致如何处理
  5. python 怎么调用 矩阵 第几行_第58集 python机器学习:混淆矩阵精度指标
  6. UDP/TCP网络传输方式
  7. AOP的底层实现-CGLIB动态代理和JDK动态代理
  8. Pandas 索引(index)/选取(select)/标签(label)操作
  9. python源码库安装_Python第三方库安装及常见问题
  10. Confluence 6 升级自定义的站点和空间布局
  11. python语音合法命名-下列哪些语句在Python中是合法的( )
  12. python最新版安装图集_[python] plist图集拆分小图
  13. Markdown从入门到精通(+软件推荐)
  14. 二元二次方程例题_二元二次方程组练习题及答案
  15. 海贼王python代码_这顶海贼王的帽子,我Python给你带上了 | 【人脸识别应用】
  16. SQL Server获取姓名拼音
  17. 怎么去面试测试工程师?
  18. 抽象类和接口的相同点和不同点
  19. matlab专业代做hslogic,MATLAB代做|FPGA代做--OLA算法的仿真与分析
  20. POJ 2316 SPIN G++

热门文章

  1. 关于win弹出cmd命令行问题
  2. ios html5 audio mp3,H5 audio 微信端 在IOS上不能播放音乐
  3. 一般纳税人企业如何合理避税?
  4. VARIANT 数据类型简单介绍
  5. springboot整合rabbitmq的发布确认,消费者手动返回ack,设置备用队列,以及面试题:rabbitmq确保消息不丢失
  6. HCIP-DATACOM H12-831(101-120)
  7. 压力测试工具-ab工具
  8. 微课程学习平台(微课平台)-特色功能(移动学习解决方案)
  9. 【LintCode 题解】小米面试算法题:搜索旋转排序数组
  10. 淘淘商城分布式电商系统项目总结