企业微信 & React & NodeJS 开发随记

这么一个文档缺失、示例缺失、平台间代码不统一、规则混乱的平台,究竟是如何发展到今天的?

前言

写这篇文章的时候,已是深夜。

刚刚 Push 了代码,等待 CI/CD 跑完的时候,抬头看已经过了零点。

立刻打开企业微信,点击按钮,不出意外的炸了。大半夜的也不方便去要 K8s 的日志,微信还不让抓包,只好作罢。

这就是我最近做开发的常态。

在企业微信开发者中心,有这么一句话:

全面、丰富、清晰的接口手册,涵盖企业内部开发、第三方应用开发以及智慧硬件开发的方方面,满足各类企业定制化开发,提供软硬一体的场景化方案。
来源: https://developer.work.weixin.qq.com/

从我这几天的开发历程,我反正是没体会到他这几个特点的。

授权

如果想要拿到 App Secret,需要如下步骤,各位自己感受一下,我光写下来我都觉得繁琐:

  1. 登录企业微信管理后台;

  2. 选择查看应用 Secret;

  3. 使用企业微信扫码验证(如果你这里用了微信扫码,很遗憾,你会被跳转到企业微信再扫一次);

  4. 在手机上点击确认;

  5. 从 企业微信管理员助手 应用下发的模版消息中复制 App Secret。

咱能能加一种常规的 2FA 方式吗?哪怕是 TOTP 呢,就这么依赖扫二维码确认身份吗?

不过话说回来,微信 PC 版一个自动登录都是催了 N 年才有的功能,也不能指望他们会在这个事情上作出太大的改动就是了。

前端

这里咱们只说一条,在尝试将企业微信的 JS-SDK 和 React 直接关联的时候,我发现他们居然只提供 <script></script> 用法!没有官方的 npm 包,就更别提 TypeScript 开发必须的 @types 包定义了。

虽然社区有用户上传了第三方的 npm 包,实际上也就是把那个 js 下载下来打了个包,也有用户制作了适用于 TypeScript 的包。我也很感谢社区有人会做这种东西,但是最终没有选择使用它们,主要是基于以下几个原因:

  • 不是官方打包,更新可能不及时;
  • 可能存在安全隐患(毕竟是第三方打包后的,一旦出现意料之外的供应链攻击会很棘手)

所以在写前端的时候,尽可能绕开需要使用 JS-SDK 的地方。

但是后端是真的绕不过去了。

后端

网页授权可信域名

我理解微信你为了安全,想要调用 OAuth2 的域名必须是 HTTPS,我也理解你需要通过验证文件的方式来验证域名所有权。

但是你为什么不能提供一个开发模式,允许开发者哪怕是暂时的允许将 localhost 作为回调域名?哪怕是加上你的传统艺能呢(扫码验证+定期失效)?

在开发初期做本地测试的时候,想通过 OAuth 拿个 code 去拉用户信息都快让我形成肌肉记忆了:

  1. 打开以 https://open.weixin.qq.com 开头的 OAuth2 链接;

  2. 复制这个打不开的回调后地址;

  3. 手动把 code 复制出来,用于调试API。

以至于我都有放弃从企业微信做 OAuth 的冲动了。我忍着做完了拿用户信息相关的调试,以为后面能一帆风顺,直到现在才知道,这不过这才是这场梦靥的开端。

接收消息服务器配置

在企业微信官方文档的接收消息部分中,提到了可以直接使用验证URL函数一步到位。顺着链接,我找到了这么一个函数:

1int VerifyURL(const string &sMsgSignature, const string &sTimeStamp, const string &sNonce, const string &sEchoStr, string &sReplyEchoStr);

然后我找到了下载库的地方,很好啊,这个库有在 npm 发布。但是仔细看看文档,是不是有什么不对?

链接的HTML是这么写的:

1<a href="https://www.npmjs.com/package/@wecom/jssdk" rel="nofollow">npm 地址</a>

但是下面提供的 npm 安装方法是这么写的:

1# with npm
2npm install @wecom/crypto
3# with yarn
4yarn add @wecom/crypto

很显然,链接中指向的库是 @wecom/jssdk,而不是 @wecom/crypto

偌大一个公司,这么低级的错误,发文档之前没人校对吗?

上述内容如有变更,可在 https://web.archive.org/web/20220326171521/https://developer.work.weixin.qq.com/document/path/95372 中查看本人写作时所阅读的版本。

就在我下载这个库之后,这个叫 VerifyURL 的函数,根本就不在库中。

同一个页面中,企业微信还提供了 C++/Python/PHP/Java/Go 的库,每一个都有实现这个URL验证的函数,都说了有一个 WXBizJsonMsgCrypt 类的存在,唯独 NodeJS 没有。贵公司对开发用的技术栈难不成还有鄙视链?功能对特定语言不实现还能堂而皇之写在文档里?

这已经不是校对的问题了,这就纯粹是不想对这个语言做对应的开发了。

当然最后还是手动实现了一份,供大家参考。各位看我实现的方式,就知道这个 @wecom/crypto 中提供的东西有多匮乏了:

 1import { Request, Response, NextFunction } from "express";
 2import * as wc from '@wecom/crypto';
 3import { wxmsgtoken, wxaeskey } from "../config";
 4
 5const wxVerify = async(req: Request, res: Response, next: NextFunction) => {
 6    var timestamp = req.query.timestamp as string,
 7        nonce = req.query.nonce as string,
 8        signature = req.query.msg_signature as string,
 9        echostr = req.query.echostr as string;
10    var msg_signature = wc.getSignature(wxmsgtoken!, timestamp, nonce, echostr);
11    if (msg_signature !== signature) {
12        res.sendStatus(400);
13        res.send('msg_signature is not valid');
14    } else {
15        var msg_decoded = wc.decrypt(wxaeskey!, echostr);
16        res.send(msg_decoded.message)
17    }
18}

总之最后还是过了接收消息服务器的验证。

总结

如果你要对微信/企业微信做开发,后端请尽可能使用 Go / Java,使用 NodeJS 等没有完备库的语言的话,use it at your own risk.