企微应用回调url实现

270 阅读1分钟

准备参数

1,登录企业微信服务商后台-->应用管理-->小程序-->TokenEncodingAESKey

2,实现URL后配置到指令回调URL

实现回调URL:

/**
 * 解码并校验 encodingAESKey
 */
function parseEncodingAESKey(encodingAESKey) {
  const key = Buffer.from(`${encodingAESKey}=`, 'base64');
  if (key.length !== 32) {
    throw new Error('invalid encodingAESKey');
  }
  const iv = key.slice(0, 16);
  return { key, iv };
}

/**
 * 解密
 * @param encrypt 密文
 */
function decrypt(encodingAESKey, encrypt) {
  const { key, iv } = parseEncodingAESKey(encodingAESKey);
  const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
  decipher.setAutoPadding(false);
  const deciphered = pkcs7Unpad(Buffer.concat([
    decipher.update(encrypt, 'base64'),
    decipher.final(),
  ]));
  const length = deciphered.readUInt32BE(16);
  return {
    message: deciphered.slice(20, length + 20).toString(),
    id: deciphered.slice(length + 20).toString(),
    random: deciphered.slice(0, 16),
  };
}

/***GET***********************************************************/
const crypto = require('crypto');
const xml2js = require('xml2js');
const EncodingAESKey = 'fooooo'; // 服务商后台生成的值
const pushToken = 'bar';
const { msg_signature, timestamp, nonce, echostr } = query;
const signature = crypto.createHash('sha1')
    .update([pushToken, timestamp, nonce, echostr].sort().join('')).digest('hex');
if (signature === msg_signature) { // 签名一致
    const deData = decrypt(EncodingAESKey, echostr);
    return { body: deData.message || deData.msg }; // GET请求返回
}


/**************************************************************/

const getSuiteAccessToken = async () => {
  if (_ENV !== 'production') { // 同步测试环境 }
  const suiteTicket = await redis.get('suiteTicket')
  const token = await redis.get('token')
  if (!token) {
      const data = {
        suite_id: SuiteID,
        suite_secret: Secret,
        suite_ticket: suiteTicket
      }
      let url = 'http://11.177.151.93/cgi-bin/service/get_suite_token'
      const res = await axios.post(url, data,
          { headers: { Host: 'in.qyapi.weixin.qq.com' } })
          .then(res => res.data)
      if (res && res.suite_access_token) {
          await redis.set('suiteTicket', res.suite_access_token, 7000);
          return res.suite_access_token
      }
  }
  return token || ''
}

// POST请求
const { elements = [] } = body;
elements.forEach(ele => {
    if (ele.name == 'xml' && ele.type == 'element') {
    const arr = ele.elements;
    arr.map(async it => {
      if (it.name == 'Encrypt' && it.type == 'element') {
        const cdata = it.elements[0].cdata;
        adata = crypto.createHash('sha1')
            .update([pushToken, timestamp, nonce, cdata].sort().join('')).digest('hex');
        if (adata === msg_signature) { // 签名一致
            const deData = decrypt(EncodingAESKey, echostr);
            const ret = await xml2js.parseStringPromise(deData.message);
            if (ret.xml.InfoType[0] === 'suite_ticket') {
                const suiteTicket = ret.xml.SuiteTicket[0];
            } else if (ret.xml.InfoType[0] === 'create_auth') { // 应用授权
                // 获取永久授权码,写数据库
            }
        }
})