nodejs 開發企業微信第三方應用入門教程

wow_worktile發表於2019-03-12

最近公司要開發企業微信端的 Worktile,以前做的是企業微信內部應用,所以只適用於私有部署客戶,而對於公有云客戶就無法使用,所有就準備開發企業微信的第三方應用,本文主要介紹在調研階段遇到的山珍海味。

開發之前你需要前註冊為第三方服務商,然後用第三方服務商的賬號建立應用,建立之後只需要管理員授權應用,第三方服務商即可為使用者提供服務。

一、註冊第三發服務商

登陸服務商官網,註冊成為服務商,並登陸服務商管理後臺。

二、配置開發資訊

在建立應用之前,首先要配置好通用開發引數

nodejs 開發企業微信第三方應用入門教程

在填寫系統事件接收 url 時,要正確響應企業微信驗證 url 的請求。這個可以參考企業微信後臺,自建應用的接收訊息的 api 設定。
在企業的管理端後臺,進入需要設定接收訊息的目標應用,點選“接收訊息”的“設定API接收”按鈕,進入配置頁面。

20190225120700.png

要求填寫應用的 URL、Token、EncodingAESKey 三個引數

  • URL 是企業後臺接收企業微信推送請求的訪問協議和地址,支援 http 或 https 協議(為了提高安全性,建議使用 https)。
  • Token 可由企業任意填寫,用於生成簽名。
  • EncodingAESKey 用於訊息體的加密,是 AES 金鑰的 Base64 編碼。

2.1 驗證 url 有效性

當點選儲存的時候,企業微信會發生一條 get 請求到填寫的 url

比如 url 設定的是https://api.worktile.com, 企業微信將傳送如下驗證請求:

請求地址:api.worktile.com/?msg_signat…

引數說明
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 並返回

密文解密過程:

  1. 對剛才生成的 AESKey 進行 base64 解碼
const EncodingAESKey = '21IpFqj8qolJbaqPqe1rVTAK5sgkaQ3GQmUKiUQLwRe';
let aesKey = Buffer.from(EncodingAESKey + '=', 'base64');
複製程式碼
  1. 對 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);
}
複製程式碼
  1. 然後返回 result 即可
res.end(result);
複製程式碼

2.2 回撥 url 驗證失敗問題

驗證 URL 時,經常會碰到 URL 驗證失敗的問題,解決思路是藉助微信企業號介面除錯工具

三、建立應用

20190228143529.png

四、測試應用

應用建立成功後,服務商可以授權 10 個測試企業

20190225143525.png

從企業微信應用市場發起授權時,企業微信給剛才應用設定的指令回撥 url 傳送一個 post 請求,比如:
https://api.worktile.com/worktile?msg_signature=b99605616153ffbfbe6ebbb500bd211e67ed714d&timestamp=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:

open.weixin.qq.com/connect/oau…

引數必須說明
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終端使用此引數判斷是否需要帶上身份資訊

auth.png

企業員工點選後,頁面將跳轉至 redirect_uri?code=CODE&state=STATE,第三方應用可根據 code 引數獲得企業員工的 corpid 與 userid。code 長度最大為 512 位元組。

6.2 獲取訪問使用者身份

請求方式:GET(HTTPS)
請求地址:qyapi.weixin.qq.com/cgi-bin/ser…

引數必須說明
access_token第三方應用的 suite_access_token,參見“獲取第三方應用憑證”
code通過成員授權獲取到的 code,最大為 512 位元組。每次成員授權帶上的 code 將不一樣,code 只能使用一次,5 分鐘未被使用自動過期。
6.2.1 獲取第三方應用的 suite_access_token

請求方式:POST(HTTPS)
請求地址: qyapi.weixin.qq.com/cgi-bin/ser…

引數是否必須說明
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)
請求地址:api.ninesix.cc/worktile?ms…

在發生授權、通訊錄變更、ticket 變化等事件時,企業微信伺服器會嚮應用的“指令回撥 URL”推送相應的事件訊息,nodejs 接收到的是 xml,解析後拿到 encrypt 欄位,然後使用上面配置通用開發引數的 url 時用的解密方式,就可以得到 suite_ticket。

20190306153526.png

6.3 獲取使用者敏感資訊

請求方式:POST(HTTPS)
請求地址:qyapi.weixin.qq.com/cgi-bin/ser…

{
   "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"
}
複製程式碼

七、使用者授權成功

首頁

home.png

詳情頁

task-detail.png

八、給使用者發訊息

我們可以給推送文字、圖片、視訊、檔案、圖文等型別。

請求方式:POST(HTTPS)
請求地址: qyapi.weixin.qq.com/cgi-bin/mes…

推送的時候需要 access_token 和 應用的 agentId,第三方服務商,可通過介面 獲取企業授權資訊 獲取該引數值,其實可以直接通過 獲取企業永久授權碼直接取到這兩個值。

在我們測試安裝應用成功之後,企業微信會 post 一條請求給指令回撥 URL,通過上面的解密方式,可以解析到 xml 中的 auth_code

20190306190621.png

然後通過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 就可以愉快的給使用者傳送訊息了。

WechatIMG5.jpg

當點選連結時,可以跳到指定任務或者日程等,只不過返回時還是在企業微信的訊息模組,並不能自動開啟第三方應用,客服回覆不支援這麼做。

九、注意事項


本文作者:王鵬

文章來源:Worktile技術部落格

歡迎訪問交流更多關於技術及協作的問題。

文章轉載請註明出處。



相關文章