详解nodejs 开发企业微信第三方应用入门教程


Posted in NodeJs onMarch 12, 2019

最近公司要开发企业微信端的 Worktile,以前做的是企业微信内部应用,所以只适用于私有部署客户,而对于公有云客户就无法使用,所有就准备开发企业微信的第三方应用,本文主要介绍在调研阶段遇到的山珍海味。

开发之前你需要前注册为第三方服务商,然后用第三方服务商的账号创建应用,创建之后只需要管理员授权应用,第三方服务商即可为用户提供服务。

一、注册第三发服务商

登陆 服务商官网 ,注册成为服务商,并登陆服务商管理后台。

二、配置开发信息

在创建应用之前,首先要配置好通用开发参数

详解nodejs 开发企业微信第三方应用入门教程

在填写系统事件接收 url 时,要正确响应企业微信验证 url 的请求。这个可以参考企业微信后台,自建应用的接收消息的 api 设置。

在企业的管理端后台,进入需要设置接收消息的目标应用,点击“接收消息”的“设置API接收”按钮,进入配置页面。

详解nodejs 开发企业微信第三方应用入门教程

要求填写应用的 URL、Token、EncodingAESKey 三个参数

  • URL 是企业后台接收企业微信推送请求的访问协议和地址,支持 http 或 https 协议(为了提高安全性,建议使用 https)。
  • Token 可由企业任意填写,用于生成签名。
  • EncodingAESKey 用于消息体的加密,是 AES 密钥的 Base64 编码。

 2.1 验证 url 有效性

当点击保存的时候,企业微信会发生一条 get 请求到填写的 url

比如 url 设置的是 https://api.worktile.com , 企业微信将发送如下验证请求:

请求地址: https://api.worktile.com/?msg_signature=ASDFQWEXZCVAQFASDFASDFSS×tamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR

参数 说明
msg_signature 企业微信加密签名,msg_signature 结合了企业填写的 token、请求中的 timestamp、nonce 参数、加密的消息体
timestamp 时间戳
nonce 随机数
echostr 加密的字符串。需要解密得到消息内容明文,解密后有random、msg_len、msg、receiveid 四个字段,其中 msg 即为消息内容明文

2.1.1 通过参数 msg_signature 对请求进行校验

首先要把刚才配置时随机生成的 token, timestamp, nonce, msg_encrypt 进行 sha1 加密,这里我们可以直接使用 npm 模块 sha1 进行加密,然后判断得到的 str 是否和 msg_signature 相等。

function sha1(str) {
 const md5sum = crypto.createHash('sha1');
 md5sum.update(str);
 const ciphertext = md5sum.digest('hex');
 return ciphertext;
}
function checkSignature(req, res, encrypt) {
 const query = req.query;
 console.log('Request URL: ', req.url);
 const signature = query.msg_signature;
 const timestamp = query.timestamp;
 const nonce = query.nonce;
 let echostr;
 console.log('encrypt', encrypt);
 if (!encrypt) {
  echostr = query.echostr;
 } else {
  echostr = encrypt;
 }
 console.log('timestamp: ', timestamp);
 console.log('nonce: ', nonce);
 console.log('signature: ', signature);
 // 将 token/timestamp/nonce 三个参数进行字典序排序
 const tmpArr = [token, timestamp, nonce, echostr];
 const tmpStr = sha1(tmpArr.sort().join(''));
 console.log('Sha1 String: ', tmpStr);
 // 验证排序并加密后的字符串与 signature 是否相等
 if (tmpStr === signature) {
  // 原样返回echostr参数内容
  const result = _decode(echostr);
  console.log('last', result);
  console.log('Check Success');
  return result;
 } else {
  console.log('Check Failed');
  return 'failed';
 }
}

2.1.2 解密 echostr 得到 msg 并返回

密文解密过程:

对刚才生成的 AESKey 进行 base64 解码

const EncodingAESKey = '21IpFqj8qolJbaqPqe1rVTAK5sgkaQ3GQmUKiUQLwRe';
let aesKey = Buffer.from(EncodingAESKey + '=', 'base64');

对 AESKey 进行 aes-256-cbc 解密

function _decode(data) {
 let aesKey = Buffer.from('21IpFqj8qolJbaqPqe1rVTAK5sgkaQ3GQmUKiUQLwRe' + '=', 'base64');
 let aesCipher = crypto.createDecipheriv("aes-256-cbc", aesKey, aesKey.slice(0, 16));
 aesCipher.setAutoPadding(false);
 let decipheredBuff = Buffer.concat([aesCipher.update(data, 'base64'), aesCipher.final()]);
 decipheredBuff = PKCS7Decoder(decipheredBuff);
 let len_netOrder_corpid = decipheredBuff.slice(16);
 let msg_len = len_netOrder_corpid.slice(0, 4).readUInt32BE(0);
 const result = len_netOrder_corpid.slice(4, msg_len + 4).toString();
 return result; // 返回一个解密后的明文-
}
function PKCS7Decoder (buff) {
 var pad = buff[buff.length - 1];
 if (pad < 1 || pad > 32) {
  pad = 0;
 }
 return buff.slice(0, buff.length - pad);
}

然后返回 result 即可

res.end(result);

2.2 回调 url 验证失败问题

验证 URL 时,经常会碰到 URL 验证失败的问题,解决思路是借助微信企业号 接口调试工具

三、创建应用

详解nodejs 开发企业微信第三方应用入门教程

四、测试应用

应用创建成功后,服务商可以授权 10 个测试企业

详解nodejs 开发企业微信第三方应用入门教程

从企业微信应用市场发起授权时,企业微信给刚才应用设置的 指令回调 url 发送一个 post 请求,比如:

https://api.worktile.com/worktile?msg_signature=b99605616153ffbfbe6ebbb500bd211e67ed714d×tamp=1551076894&nonce=1551709703 ,直接返回成功即可。

各个事件的回调,服务商在收到推送后都必须直接返回字符串 “success”,若返回值不是 “success”,企业微信会把返回内容当作错误信息。

app.post('/worktile', function (req, res) {
 console.log('req.body', req.body);
 res.send('success');
});

测试应用注意事项

  1. 用于安装测试的企业微信帐号需服务商自行注册,每个应用支持同时添加 10 个测试企业微信账号
  2. 安装测试的企业微信帐号使用的是当前的应用配置信息,后续的修改不会进行同步;如需更新应用信息请重新授权安装
  3. 同一企业微信帐号,不支持同时安装测试应用和正式发布的应用

五、应用上线

已认证企业微信的服务商,可进入应用管理—点击提交上线—勾选应用—提交上线。

六、用户网页授权登录

6.1 构造第三方应用网页授权链接

如果第三方应用需要在打开的网页里面携带用户的身份信息,第一步需要构造如下的链接来获取 code:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

参数 必须 说明
appid 第三方应用 id(即 ww 或 wx 开头的 suite_id)。注意与企业的网页授权登录不同
redirect_uri 授权后重定向的回调链接地址,请使用 urlencode 对链接进行处理 ,注意域名需要设置为第三方应用的可信域名
response_type 返回类型,此时固定为:code
scope 应用授权作用域。snsapi_base:静默授权,可获取成员的基础信息(UserId与DeviceId);snsapi_userinfo:静默授权,可获取成员的详细信息,但不包含手机、邮箱等敏感信息;snsapi_privateinfo:手动授权,可获取成员的详细信息,包含手机、邮箱等敏感信息。
state 重定向后会带上 state 参数,企业可以填写 a-zA-Z0-9 的参数值,长度不可超过 128 个字节
#wechat_redirect 终端使用此参数判断是否需要带上身份信息

详解nodejs 开发企业微信第三方应用入门教程

企业员工点击后,页面将跳转至 redirect_uri?code=CODE&state=STATE,第三方应用可根据 code 参数获得企业员工的 corpid 与 userid。code 长度最大为 512 字节。

6.2 获取访问用户身份

请求方式:GET(HTTPS)

请求地址: https://qyapi.weixin.qq.com/cgi-bin/service/getuserinfo3rd?access_token=SUITE_ACCESS_TOKEN&code=CODE

参数 必须 说明
access_token 第三方应用的 suite_access_token,参见“获取第三方应用凭证”
code 通过成员授权获取到的 code,最大为 512 字节。每次成员授权带上的 code 将不一样,code 只能使用一次,5 分钟未被使用自动过期。

 6.2.1 获取第三方应用的 suite_access_token

请求方式:POST(HTTPS)

请求地址: https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token

参数 是否必须 说明
suite_id 以 ww 或 wx 开头应用 id(对应于旧的以 tj 开头的套件 id)
suite_secret 应用 secret
suite_ticket 企业微信后台推送的 ticket

由于第三方服务商可能托管了大量的企业,其安全问题造成的影响会更加严重,故 API 中除了合法来源 IP 校验之外,还额外增加了 suite_ticket 作为安全凭证。

获取 suite_access_token 时,需要 suite_ticket 参数。suite_ticket 由企业微信后台定时推送给“指令回调 URL”,每十分钟更新一次,见推送 suite_ticket 。

suite_ticket 实际有效期为 30 分钟,可以容错连续两次获取 suite_ticket 失败的情况,但是请永远使用最新接收到的 suite_ticket。

通过本接口获取的 suite_access_token 有效期为 2 小时,开发者需要进行缓存,不可频繁获取。

6.2.2 获取推送 suite_ticket

企业微信服务器会定时(每十分钟)推送 ticket。ticket 会实时变更,并用于后续接口的调用。

请求方式:POST(HTTPS)

请求地址: https://api.ninesix.cc/worktile?msg_signature=87276aaf15a13e1eb2ebb6d93732ca668c3ddef8×tamp=1551850300&nonce=1551051655

在发生授权、通讯录变更、ticket 变化等事件时,企业微信服务器会向应用的“指令回调 URL”推送相应的事件消息,nodejs 接收到的是 xml,解析后拿到 encrypt 字段,然后使用上面配置通用开发参数的 url 时用的解密方式,就可以得到 suite_ticket。

详解nodejs 开发企业微信第三方应用入门教程

6.3 获取用户敏感信息

请求方式:POST(HTTPS)

请求地址: https://qyapi.weixin.qq.com/cgi-bin/service/getuserdetail3rd?access_token=SUITE_ACCESS_TOKEN

{
  "user_ticket": "USER_TICKET"
}

参数 必须 说明
access_token 第三方应用的 suite_access_token,参见“获取第三方应用凭证”
user_ticket 成员票据

返回结果:

{
  "errcode": 0,
  "errmsg": "ok",
  "corpid": "wwxxxxxxyyyyy",
  "userid": "lisi",
  "name": "李四",
  "mobile": "15913215421",
  "gender": "1",
  "email": "xxx@xx.com",
  "avatar": "http://shp.qpic.cn/bizmp/xxxxxxxxxxx/0",
  "qr_code": "https://open.work.weixin.qq.com/wwopen/userQRCode?vcode=vcfc13b01dfs78e981c"
}

七、用户授权成功

首页

详解nodejs 开发企业微信第三方应用入门教程

详情页

详解nodejs 开发企业微信第三方应用入门教程

八、给用户发消息

我们可以给推送文本、图片、视频、文件、图文等类型。

请求方式:POST(HTTPS)

请求地址: https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=ACCESS_TOKEN

推送的时候需要 access_token 和 应用的 agentId,第三方服务商,可通过接口 获取企业授权信息 获取该参数值,其实可以直接通过获取企业永久授权码 直接取到这两个值。

在我们测试安装应用成功之后,企业微信会 post 一条请求给指令回调 URL,通过上面的解密方式,可以解析到 xml 中的 auth_code

详解nodejs 开发企业微信第三方应用入门教程

然后通过 https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=SUITE_ACCESS_TOKENauth_code 可以获取到 access_token 和 agentId,返回的 agent 是一个数组,但仅旧的多应用套件授权时会返回多个agent,对新的单应用授权,永远只返回一个 agent。

再通过 access_token 和 agentId 就可以愉快的给用户发送消息了。

详解nodejs 开发企业微信第三方应用入门教程

当点击链接时,可以跳到指定任务或者日程等,只不过返回时还是在企业微信的消息模块,并不能自动打开第三方应用,客服回复不支持这么做。

九、注意事项

api 可能有时效性,如有差异,以官方 api 为准。

完整 demo

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

NodeJs 相关文章推荐
nodejs教程 安装express及配置app.js文件的详细步骤
May 11 NodeJs
NodeJS Express框架中处理404页面一个方式
May 28 NodeJs
轻松创建nodejs服务器(7):阻塞操作的实现
Dec 18 NodeJs
轻松创建nodejs服务器(10):处理POST请求
Dec 18 NodeJs
浅谈Nodejs应用主文件index.js
Aug 28 NodeJs
浅谈NodeJs之数据库异常处理
Oct 25 NodeJs
详解nodeJs文件系统(fs)与流(stream)
Jan 24 NodeJs
nodejs读取本地中文json文件出现乱码解决方法
Oct 10 NodeJs
nodejs检测因特网是否断开的解决方案
Apr 17 NodeJs
nodejs语言实现验证码生成功能的示例代码
Oct 13 NodeJs
nodeJS与MySQL实现分页数据以及倒序数据
Jun 05 NodeJs
浅谈Node的内存泄露问题
May 06 NodeJs
详解NodeJS Https HSM双向认证实现
Mar 12 #NodeJs
NodeJs入门教程之定时器和队列
Mar 08 #NodeJs
nodejs npm错误Error:UNKNOWN:unknown error,mkdir 'D:\Develop\nodejs\node_global'at Error
Mar 02 #NodeJs
nodejs同步调用获取mysql数据时遇到的大坑
Mar 02 #NodeJs
Nodejs中怎么实现函数的串行执行
Mar 02 #NodeJs
Nodejs让异步变成同步的方法
Mar 02 #NodeJs
nodejs使用async模块同步执行的方法
Mar 02 #NodeJs
You might like
ThinkPHP之getField详解
2014/06/20 PHP
PHP SESSION的增加、删除、修改、查看操作
2015/03/20 PHP
PHP分页初探 一个最简单的PHP分页代码的简单实现
2016/06/21 PHP
BOOM vs RR BO5 第二场 2.14
2021/03/10 DOTA
一个小型js框架myJSFrame附API使用帮助
2008/06/28 Javascript
用js模拟JQuery的show与hide动画函数代码
2010/09/20 Javascript
js的hasownproperty使用示例
2014/03/02 Javascript
js实现进度条的方法
2015/02/13 Javascript
jQuery实现文本展开收缩特效
2015/06/03 Javascript
jQuery+CSS3实现3D立方体旋转效果
2015/11/10 Javascript
jQuery拖动元素并对元素进行重新排序
2015/12/30 Javascript
jQuery遍历节点方法汇总(推荐)
2017/05/13 jQuery
Vue数据绑定实例写法
2019/08/06 Javascript
jquery插件实现轮播图效果
2020/10/19 jQuery
python多线程扫描端口示例
2014/01/16 Python
《Python之禅》中对于Python编程过程中的一些建议
2015/04/03 Python
[原创]pip和pygal的安装实例教程
2017/12/07 Python
5款非常棒的Python工具
2018/01/05 Python
Python退火算法在高次方程的应用
2018/07/26 Python
浅谈pycharm下找不到sqlalchemy的问题
2018/12/03 Python
用Python读取几十万行文本数据
2018/12/24 Python
TensorFlow车牌识别完整版代码(含车牌数据集)
2019/08/05 Python
Python判断三段线能否构成三角形的代码
2020/04/12 Python
使用python实现下载我们想听的歌曲,速度超快
2020/07/09 Python
Python基于pillow库实现生成图片水印
2020/09/14 Python
python 第三方库paramiko的常用方式
2021/02/20 Python
Python使用Turtle模块绘制国旗的方法示例
2021/02/28 Python
你在项目中用到了xml技术的哪些方面?如何实现的?
2014/01/26 面试题
银行个人求职自荐信范文
2013/12/16 职场文书
学校读书活动总结
2014/06/30 职场文书
地球物理学专业推荐信
2014/09/08 职场文书
李白故里导游词
2015/02/12 职场文书
2016年学校“6﹒26国际禁毒日”宣传活动总结
2016/04/05 职场文书
vue3如何优雅的实现移动端登录注册模块
2021/03/29 Vue.js
Python序列化与反序列化相关知识总结
2021/06/08 Python
css3 文字断裂效果
2022/04/22 HTML / CSS