准备参数
1,登录企业微信服务商后台-->应用管理-->小程序-->Token、EncodingAESKey
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') { // 应用授权
// 获取永久授权码,写数据库
}
}
})