shop-back-end/doc/simpread-二:如何接收企业微信回调 - 教程 ...

9.9 KiB
Raw Permalink Blame History

本文由 简悦 SimpRead 转码, 原文地址 developer.work.weixin.qq.com

在上一节教程中,我们创建了一个第三方应用,得到了对应的 suite_id/ suite_secret 信息,但是并没有对数据回调和接口回调进行响应,网页提示验证不通过。

在上一节教程中,我们创建了一个第三方应用,得到了对应的 suite_id/ suite_secret 信息,但是并没有对数据回调和接口回调进行响应,网页提示验证不通过。
在本节课程中,我们将完善服务端的逻辑,接收企业微信的回调,使回调 URL 的配置能够通过验证。

通过本节教程,开发者将了解:

  1. 如何接收企业微信的指令和数据回调
  2. 如何对企业微信的回调进行验证
  3. 如何实现第三方应用的回调服务

准备工作

在开始本教程内容前,开发者需要做如下准备:

  • 已创建第三方应用,并在第三方【应用详情】-【回调配置】找到对应的 Token 和 EncodingAESKey可参考 一:如何创建第三方应用
  • 在回调的实现逻辑中需要进行加解密计算,企业微信已经提供了 C++ Python PHP Java Go C# Node.js 等语言版本的加解密库,并且均提供了解密、加密、验证 URL 三个接口,开发者可以根据开发需要 下载加解密库
  • 根据实际开发需要,搭建内网穿透服务。

本节教程将以 Node.js 作为代码语言示例,讲解对应的操作流程。

回调的实现类型

在配置应用的开发信息时,我们已经知道了第三方应用的回调配置有数据回调和指令回调两种。

数据回调,用于接收托管企业微信应用的用户消息、进入应用事件、通讯录变更事件。
指令回调,用于接收应用授权变更事件(应用添加、删除、修改)以及 ticket 参数ticket 说明详 API 接口说明。

对于数据回调和指令回调的两个 URL ,在服务端的实现时,都必须同时支持 HttpGet 以及 HttpPost 两种能力。

1、Get 类型

仅用于在应用创建配置应用信息时的验证,企业微信服务端会向回调 URL 发起一个 Get 请求,当该回调 URL 按照约定进行了响应后,表明第三方服务具备解析企业微信推送消息的能力。

2、Post 类型

用于实际的业务请求,比如应用菜单的点击事件,用户消息等。当有回调的行为发生时,企业微信服务端会向该回调 URL 发起一个 Post 请求,同时数据会已加密的形式推送到该回调 URL第三方服务商接受信息、解密信息处理业务逻辑并且按照约定进行响应即可。

回调内网穿透

回调服务的 URL 是一个公网的 URL如果在实际进行应用开发时代码和服务是部署在本地机器的话那么需要搭建一个内网穿透服务要让企业微信服务器发来的数据回调和指令回调能够到达本地服务。

三方应用开发的内网穿透,可以使用官方提供的 内网穿透工具 进行快速调试和验证。

目前社区有许多较成熟的内网穿透解决方案,开发者可根据实际需要进行选择。

Get 回调实现

根据创建第三方应用时填写的回调 URL假设为 http://myapp.com/callback/command ,我们将来实现这个 URL 的 get 请求路由,并且按照要求响应。

1、实现 get 路由

数据回调 URL 和 指令回调 URL 均需要支持 HttpGet 形式的请求回调。

const express = require('express');
const router = express.Router();
// get方式 用于验证
router.get('/callback/data', validateCallback);
router.get('/callback/command', validateCallback);

2、解析回调参数

企业微信回调服务会通过 HttpGet 的形式请求回调 URL并将 msg_signature、timestamp、nonce、echostr 以 query 参数的形式传递过来。我们在回调 URL 的逻辑中,解析出这些参数。

// 从 query 中获取相关参数
const { msg_signature, timestamp, nonce, echostr } = req.query;

3、计算签名并校验

根据在上一步获取的参数 timestamp、nonce、echostr连同 Token 一起,使用加解密库的加密函数重新加密计算签名。

// 引入官方的加解密库
const crypto = require('@wecom/crypto');
// 在应用详情页找到对应的token
const token = '';
// 重新计算签名
const signature = crypto.getSignature(token, timestamp, nonce, echostr);
console.log('signature', signature);

4、解密回调的加密串并将内容返回

通过 EncodingAESKey 和解析出来的 echostr 作为参数,使用加解密库的解密函数,解密出对应的 message并立即以明文的形式直接返回。

// 校验签名是否正确
if (signature === msg_signature) {
    console.info('签名验证成功')
    // 在应用详情页找到对应的EncodingAESKey
    const aeskey = '';
    // 如果签名校验正确,解密 message
    const { message } = crypto.decrypt(aeskey, echostr);
    console.log('message', message);
    // 返回 message 信息
    res.send(message);
}

到这里,我们已经完成了回调 URL 的 get 实现,在【应用详情】-【回调配置】重新点击编辑保存,便可以重新触发指令回调 URL 和数据回调 URL 的验证,确保网页不再提示 “服务商未响应请求,将无法获取用户事件回调”。

想要了解更多 Get 回调的实现,可以参考 支持 http-get 请求验证 url 有效性

Post 回调实现

1、实现 post 路由

数据回调 URL 和 指令回调 URL 均需要支持 HttpPost 形式的请求回调。

const express = require('express');
const router = express.Router();
// post方式 用于授权、业务数据回调
router.post('/callback/command', function (req, res, next) {});

2、解析参数并校验签名

企业微信回调服务会通过 HttpPost 的形式请求该回调 URL并将 msg_signature、timestamp、nonce 以 query 参数的形式传递过来,像 HttpGet 形式一样,需要先对参数进行解析,并且校验签名有效。
获取的参数 timestamp、nonce、echostr连同 Token 一起,使用加解密库的加密函数重新加密计算签名。

// 引入官方的加解密库
const crypto = require('@wecom/crypto');
// 从 query 中获取相关参数
const { msg_signature, timestamp, nonce } = req.query;
// 在应用详情页找到对应的token
const token = '';
// 重新计算签名
const signature = crypto.getSignature(token, timestamp, nonce, echostr);
console.log('signature', signature);

3、解密密文消息

在 HttpPost 回调中,密文内容是以 XML 字符串的形式放在 RawBody 中,通过对该 XML 字符的解析读取出对应的加密消息 Encrypt。
通过 EncodingAESKey 和加密消息 Encrypt 作为参数,使用加解密库的解密函数,解密出对应的 message。
而 message 仍然是一个 XML 字符串格式,再次对该 XML 解析,便可以得到本次 HttpPost 回调实际的数据信息。

// 解析 xml 的字符串内容
let wholeXML = await getRawBody(req, {
    length: req.headers['content-length'],
    limit: '1mb',
    encoding: 'utf-8'
});
// 将 xml 解析成 json
let formatJson = await parseXML(wholeXML);
// 得到加密消息体
let encrypt = formatJson.Encrypt;
// 在应用详情页找到对应的EncodingAESKey
const aeskey = '';
// 将加密消息体进行解密,解密后仍旧是 xml 字符串
let messageXML = crypto.decrypt(aeskey, encrypt);
// 把解密后 xml 消息体字符串,解析成 json
let callbackDataBody = await parseXML(messageXML.message);
console.log('CallbackData', callbackDataBody);

4、响应本次请求

不同的业务回调要求返回不同内容,在本节中我们直接返回 success 字符串即可。

想要了解企业微信回调设置的更详细信息,可以参考文档 回调配置

到这里,我们已经完成了第三方应用的 HttpPost 回调配置逻辑框架,在下一节教程,我们就可以利用这个指令回调实现 suite_ticket 的接收,从而获得 suite_access_token。