详解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 相关文章推荐
基于 Docker 开发 NodeJS 应用
Jul 30 NodeJs
Nodejs高扩展性的模板引擎 functmpl简介
Feb 13 NodeJs
NodeJS仿WebApi路由示例
Feb 28 NodeJs
使用nodejs爬取前程无忧前端技能排行
May 06 NodeJs
深入浅析Nodejs的Http模块
Jun 20 NodeJs
详解nodejs实现本地上传图片并预览功能(express4.0+)
Jun 28 NodeJs
nodejs项目windows下开机自启动的方法
Nov 22 NodeJs
nodejs使用http模块发送get与post请求的方法示例
Jan 08 NodeJs
nodeJs爬虫的技术点总结
May 13 NodeJs
nodejs实现范围请求的实现代码
Oct 12 NodeJs
Nodejs实现多文件夹文件同步
Oct 17 NodeJs
NodeJS有难度的面试题(能答对几个)
Oct 09 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
Oracle Faq(Oracle的版本)
2006/10/09 PHP
114啦源码(114la)不能生成地方房产和地方报刊问题4级页面0字节的解决方法
2012/01/12 PHP
php封装好的人民币数值转中文大写类
2015/12/20 PHP
Laravel实现搜索的时候分页并携带参数
2019/10/15 PHP
使Ext的Template可以解析二层的json数据的方法
2007/12/22 Javascript
javascript 类方法定义还是有点区别
2009/04/15 Javascript
Chosen 基于jquery的选择框插件使用方法
2012/05/30 Javascript
JQuery事件e参数的方法preventDefault()取消默认行为
2013/09/26 Javascript
js中数组(Array)的排序(sort)注意事项说明
2014/01/24 Javascript
JS简单限制textarea内输入字符数量的方法
2015/10/14 Javascript
BootStrap 动态添加验证项和取消验证项的实现方法
2016/09/28 Javascript
vue组件实例解析
2017/01/10 Javascript
js中的触发事件对象event.srcElement与event.target详解
2017/03/15 Javascript
Django1.7+JQuery+Ajax验证用户注册集成小例子
2017/04/08 jQuery
vue.js移动端app之上拉加载以及下拉刷新实战
2017/09/11 Javascript
JS实现带动画的回到顶部效果
2017/12/28 Javascript
微信小程序图片轮播组件gallery slider使用方法详解
2018/01/31 Javascript
使用 vue 实例更好的监听事件及vue实例的方法
2019/04/22 Javascript
8个有意思的JavaScript面试题
2019/07/30 Javascript
Elasticsearch实现复合查询高亮结果功能
2019/09/10 Javascript
vue 自定义组件的写法与用法详解
2020/03/04 Javascript
[55:02]2014 DOTA2国际邀请赛中国区预选赛 HGT VS Orenda
2014/05/21 DOTA
[01:13]2015国际邀请赛线下观战现场
2015/08/08 DOTA
python 打印出所有的对象/模块的属性(实例代码)
2016/09/11 Python
详解Python中的静态方法与类成员方法
2017/02/28 Python
tensorflow获取变量维度信息
2018/03/10 Python
Python中shapefile转换geojson的示例
2019/01/03 Python
python软件测试Jmeter性能测试JDBC Request(结合数据库)的使用详解
2021/01/26 Python
美国领先的水果篮送货公司和新鲜水果供应商:The Fruit Company
2018/02/13 全球购物
PHP经典面试题
2016/09/03 面试题
2014广电局实施党的群众路线教育实践活动方案思想汇报
2014/09/22 职场文书
学校党委干部个人对照检查材料思想汇报
2014/10/09 职场文书
建国大业观后感600字
2015/06/01 职场文书
2016年大学迎新工作总结
2015/10/14 职场文书
新学期小学班主任工作计划
2019/06/21 职场文书
Django开发RESTful API实现增删改查(入门级)
2021/05/10 Python