最近公司要開發企業微信端的 Worktile,之前作的是企業微信內部應用,因此只適用於私有部署客戶,而對於公有云客戶就沒法使用。全部本文就準備開發企業微信的第三方應用,主要介紹在調研階段遇到的山珍海味。node
開發以前你須要前註冊爲第三方服務商,而後用第三方服務商的帳號建立應用,建立以後只須要管理員受權應用,第三方服務商便可爲用戶提供服務。git
登錄服務商官網,註冊成爲服務商,並登錄服務商管理後臺。github
在建立應用以前,首先要配置好通用開發參數npm
在填寫系統事件接收 url 時,要正確響應企業微信驗證 url 的請求。這個能夠參考企業微信後臺,自建應用的接收消息的 api 設置。
在企業的管理端後臺,進入須要設置接收消息的目標應用,點擊「接收消息」的「設置API接收」按鈕,進入配置頁面。json
要求填寫應用的 URL、Token、EncodingAESKey 三個參數c#
當點擊保存的時候,企業微信會發生一條 get 請求到填寫的 urlsegmentfault
好比 url 設置的是https://api.worktile.com
, 企業微信將發送以下驗證請求:api
請求地址:https://api.worktile.com/?msg...×tamp=13500001234&nonce=123412323&echostr=ENCRYPT_STR數組
參數 | 說明 |
---|---|
msg_signature | 企業微信加密簽名,msg_signature 結合了企業填寫的 token、請求中的 timestamp、nonce 參數、加密的消息體 |
timestamp | 時間戳 |
nonce | 隨機數 |
echostr | 加密的字符串。須要解密獲得消息內容明文,解密後有random、msg_len、msg、receiveid 四個字段,其中 msg 即爲消息內容明文 |
首先要把剛纔配置時隨機生成的 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'; } }
密文解密過程:
const EncodingAESKey = '21IpFqj8qolJbaqPqe1rVTAK5sgkaQ3GQmUKiUQLwRe'; let aesKey = Buffer.from(EncodingAESKey + '=', 'base64');
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); }
res.end(result);
驗證 URL 時,常常會碰到 URL 驗證失敗的問題,解決思路是藉助微信企業號接口調試工具
應用建立成功後,服務商能夠受權 10 個測試企業
從企業微信應用市場發起受權時,企業微信給剛纔應用設置的指令回調 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'); });
測試應用注意事項
已認證企業微信的服務商,可進入應用管理—點擊提交上線—勾選應用—提交上線。
若是第三方應用須要在打開的網頁裏面攜帶用戶的身份信息,第一步須要構造以下的連接來獲取 code:
https://open.weixin.qq.com/co...
參數 | 必須 | 說明 |
---|---|---|
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 | 是 | 終端使用此參數判斷是否須要帶上身份信息 |
企業員工點擊後,頁面將跳轉至 redirect_uri?code=CODE&state=STATE,第三方應用可根據 code 參數得到企業員工的 corpid 與 userid。code 長度最大爲 512 字節。
請求方式:GET(HTTPS)
請求地址:https://qyapi.weixin.qq.com/c...
參數 | 必須 | 說明 |
---|---|---|
access_token | 是 | 第三方應用的 suite_access_token,參見「獲取第三方應用憑證」 |
code | 是 | 經過成員受權獲取到的 code,最大爲 512 字節。每次成員受權帶上的 code 將不同,code 只能使用一次,5 分鐘未被使用自動過時。 |
請求方式:POST(HTTPS)
請求地址: https://qyapi.weixin.qq.com/c...
參數 | 是否必須 | 說明 |
---|---|---|
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 小時,開發者須要進行緩存,不可頻繁獲取。
企業微信服務器會定時(每十分鐘)推送 ticket。ticket 會實時變動,並用於後續接口的調用。
請求方式:POST(HTTPS)
請求地址:https://api.ninesix.cc/workti...
在發生受權、通信錄變動、ticket 變化等事件時,企業微信服務器會嚮應用的「指令回調 URL」推送相應的事件消息,nodejs 接收到的是 xml,解析後拿到 encrypt 字段,而後使用上面配置通用開發參數的 url 時用的解密方式,就能夠獲得 suite_ticket。
請求方式:POST(HTTPS)
請求地址:https://qyapi.weixin.qq.com/c...
{ "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" }
首頁
詳情頁
咱們能夠給推送文本、圖片、視頻、文件、圖文等類型。
請求方式:POST(HTTPS)
請求地址: https://qyapi.weixin.qq.com/c...
推送的時候須要 access_token 和 應用的 agentId,第三方服務商,可經過接口 獲取企業受權信息 獲取該參數值,其實能夠直接經過 獲取企業永久受權碼直接取到這兩個值。
在咱們測試安裝應用成功以後,企業微信會 post 一條請求給指令回調 URL,經過上面的解密方式,能夠解析到 xml 中的 auth_code
而後經過https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=SUITE_ACCESS_TOKEN
和 auth_code
能夠獲取到 access_token 和 agentId,返回的 agent 是一個數組,但僅舊的多應用套件受權時會返回多個agent,對新的單應用受權,永遠只返回一個 agent。
再經過 access_token 和 agentId 就能夠愉快的給用戶發送消息了。
當點擊連接時,能夠跳到指定任務或者日程等,只不過返回時仍是在企業微信的消息模塊,並不能自動打開第三方應用,客服回覆不支持這麼作。
本文做者:王鵬
文章來源:Worktile技術博客
歡迎訪問交流更多關於技術及協做的問題。
文章轉載請註明出處。