企业微信私有化/政务微信JSAPI接入全过程 (node后端,VUE前端)

2,404 阅读5分钟

开始前

第一步:需要在相应的企业微信私有化的管理端新建一个应用

juejin1.png

第二步:在自建应用里面配置好可信域名:open.gov.weixin.qq.com (你自己服务器的域名)

juejin2.png

juejin3.png

配置好这两步之后就可以进行后端签名了

node后端

第一步:需要获取access_token,根据官方文档的接口去获取:

获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token) access_token是应用的全局唯一票据,调用接口时需携带access_token。 access_token需要用CorpID和Secret来换取,不同的Secret会返回不同的access_token。正常情况下access_token有效期为7200秒,有效期内重复获取返回相同结果。access_token至少保留512字节的存储空间。

请求说明 Https请求方式: GET {{你的私有化地址域名}}/cgi-bin/gettoken?corpid=id&corpsecret=secrect

url : '你的私有化地址域名'+'/cgi-bin/gettoken?corpid=' + '你的企业corpid' + '&corpsecret=' + '你的自建应用的secret',

return new Promise(function(resolve, reject) {
    request(this.url, function(error, response, body) {
        error ? reject(error) : resolve({ response: response, body: body })
    })
    

第二步:分别去获取config和agentconfig的ticket,注意这两个请求是不一样的

用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):

config的ticket获取请求: {{你的私有化地址域名}}/cgi-bin/get_jsapi_ticket?access_token=ACCESS_TOKEN

agentconfig的ticket获取请求: {{你的私有化地址域名}}/cgi-bin/ticket/get?access_token=ACCESS_TOKEN&type=agent_config

var conifgurlurl='你的私有化地址'+'/cgi-bin/get_jsapi_ticket?access_token='+'你第一步获取的access_token'

var agentConfigUrl= '你的私有化地址' + '/cgi-bin/ticket/get?access_token='+'你第一步获取的access_token'

 config = new Promise(function(resolve, reject) {
       request(conifgurl, function(error, response, body) {
            error ? reject(error) : resolve({ response: response, body: body })
        })
  })
                            
  agentConfig = new Promise(function(resolve, reject) {
          request(agentconfigUrl, function(error, response, body) {
              error ? reject(error) : resolve({ response: response, body: body })
       })
       
 //同时放进Promise去请求
return  Promise.all([config, agentConfig])
   .then(function(result) {


//处理请求结果生成相应的签名  
var interItem = {}
interItem.noncestr = weixin.randomString(17)
interItem.timestamp = Date.parse(new Date()) / 1000

let configItem = {
	"jsapi_ticket": JSON.parse(result[0].body).ticket,
	"noncestr": interItem.noncestr,
	"timestamp": interItem.timestamp,
	//interItem.reqUrl你自己窗口的请求地址
	"url": interItem.reqUrl
}; 

let agentconfigItem = {
	"jsapi_ticket": JSON.parse(result[1].body).ticket,
	"noncestr": interItem.noncestr,
	"timestamp": interItem.timestamp,
	"url": interItem.reqUrl
};

let signstr = "";
for (let item in configItem) {
	signstr += item + "=" + configItem[item] + "&"
}
signstr = signstr.substr(0, signstr.length - 1);

interItem.signature = weixin.sha1(signstr)

let agentsignstr = "";
for (let item in agentconfigItem) {
	agentsignstr += item + "=" + agentconfigItem[item] + "&"
}
agentsignstr = agentsignstr.substr(0, agentsignstr.length - 1);

interItem.agentSignature = weixin.sha1(agentsignstr)

//将请求的结果放进对应的res对象并返回
var res = {

	noncestr : interItem.noncestr,

	timestamp : interItem.timestamp,

	signature : interItem.signature,

	agentSignature : interItem.agentSignature
}
   return  res
})
}).catch((error) => {
console.log(error)
})

其中所使用到的相关函数:

//签名辅助函数
const utils = require('@tencent/mail.utils')
module.exports = {
//生成随机字符串
 randomString(len) {
    len = len || 32;
    var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
    var maxPos = $chars.length;
    var pwd = '';
    for (let i = 0; i < len; i++) {
        pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
    }
    return pwd;
},

encodeUTF8(s) {
    var i, r = [],
        c, x;
    for (i = 0; i < s.length; i++)
        if ((c = s.charCodeAt(i)) < 0x80) r.push(c);
        else if (c < 0x800) r.push(0xC0 + (c >> 6 &amp; 0x1F), 0x80 + (c &amp; 0x3F));
    else {
        if ((x = c ^ 0xD800) >> 10 == 0) //对四字节UTF-16转换为Unicode
            c = (x << 10) + (s.charCodeAt(++i) ^ 0xDC00) + 0x10000,
            r.push(0xF0 + (c >> 18 &amp; 0x7), 0x80 + (c >> 12 &amp; 0x3F));
        else r.push(0xE0 + (c >> 12 &amp; 0xF));
        r.push(0x80 + (c >> 6 &amp; 0x3F), 0x80 + (c &amp; 0x3F));
    };
    return r;
},

// 字符串加密成 hex 字符串
 sha1(s) {
    var data = new Uint8Array(this.encodeUTF8(s))
    var i, j, t;
    var l = ((data.length + 8) >>> 6 << 4) + 16,
        s = new Uint8Array(l << 2);
    s.set(new Uint8Array(data.buffer)), s = new Uint32Array(s.buffer);
    for (t = new DataView(s.buffer), i = 0; i < l; i++) s[i] = t.getUint32(i << 2);
    s[data.length >> 2] |= 0x80 << (24 - (data.length &amp; 3) * 8);
    s[l - 1] = data.length << 3;
    var w = [],
        f = [
            function() {
                return m[1] &amp; m[2] | ~m[1] &amp; m[3];
            },
            function() {
                return m[1] ^ m[2] ^ m[3];
            },
            function() {
                return m[1] &amp; m[2] | m[1] &amp; m[3] | m[2] &amp; m[3];
            },
            function() {
                return m[1] ^ m[2] ^ m[3];
            }
        ],
        rol = function(n, c) {
            return n << c | n >>> (32 - c);
        },
        k = [1518500249, 1859775393, -1894007588, -899497514],
        m = [1732584193, -271733879, null, null, -1009589776];
    m[2] = ~m[0], m[3] = ~m[1];
    for (i = 0; i < s.length; i += 16) {
        var o = m.slice(0);
        for (j = 0; j < 80; j++)
            w[j] = j < 16 ? s[i + j] : rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1),
            t = rol(m[0], 5) + f[j / 20 | 0]() + m[4] + w[j] + k[j / 20 | 0] | 0,
            m[1] = rol(m[1], 30), m.pop(), m.unshift(t);
        for (j = 0; j < 5; j++) m[j] = m[j] + o[j] | 0;
    };
    t = new DataView(new Uint32Array(m).buffer);
    for (var i = 0; i < 5; i++) m[i] = t.getUint32(i << 2);

    var hex = Array.prototype.map.call(new Uint8Array(new Uint32Array(m).buffer), function(e) {
        return (e < 16 ? "0" : "") + e.toString(16);
    }).join("");
    return hex;
}

}

VUE前端处理

config的相关配置:

 wx.config({
            beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题
            debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
            appId: resData[0].corpid, // 必填,企业微信的corpID
            timestamp: this.res.timestamp, // 必填,生成签名的时间戳
            nonceStr:this.res.noncestr, // 必填,生成签名的随机串
            signature: this.res.signature, // 必填,签名,见 附录-JS-SDK使用权限签名算法
            jsApiList: ["getNetworkType","onHistoryBack","scanQRCode","chooseImage","getLocalImgData","agentConfig","onMenuShareAppMessage","onMenuShareWechat",
                        "checkJsApi","openChatWithMsg","shareAppMessage","shareWechatMessage","onMenuShareTimeline","previewImage","checkIsSupportSoterAuthentication",
                        "uploadImage","downloadImage","previewFile","chooseVideo","uploadVideo","downloadVideo","startRecord","stopRecord","playVoice","pauseVoice",
                        "stopVoice","startRecordVoiceBuffer","stopRecordVoiceBuffer","translateVoice","uploadVoice","downloadVoice","onVoiceRecordEnd","onVoicePlayEnd",
                        "onRecordBufferReceived","startWifi","stopWifi","connectWifi","getWifiList","onGetWifiList","onWifiConnected","getConnectedWifi",
                        "openBluetoothAdapter","closeBluetoothAdapter","getBluetoothAdapterState","onBluetoothAdapterStateChange","startBluetoothDevicesDiscovery",
                        "stopBluetoothDevicesDiscovery","getBluetoothDevices","onBluetoothDeviceFound","getConnectedBluetoothDevices","createBLEConnection",
                        "closeBLEConnection","onBLEConnectionStateChange","getBLEDeviceServices","getBLEDeviceCharacteristics","readBLECharacteristicValue",
                        "writeBLECharacteristicValue","notifyBLECharacteristicValueChange","onBLECharacteristicValueChange","startBeaconDiscovery","stopBeaconDiscovery",
                        "onBeaconUpdate","getBeacons","onBeaconServiceChange","setClipboardData","getClipboardData","onNetworkStatusChange","openLocation",
                        "getLocation","startAutoLBS","stopAutoLBS","onLocationChange","hideOptionMenu","showOptionMenu","closeWindow","hideMenuItems","showMenuItems",
                        "hideAllNonBaseMenuItem","showAllNonBaseMenuItem","onUserCaptureScreen","openUrl","showWatermark","hideWatermark","checkIsSoterEnrolledInDevice",
                        "startSoterAuthentication","bioassayAuthentication","launch3rdApp","getInstallState","request3rdApp","ocr"] // 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
        });

agentconfig的相关配置:

 wx.invoke("agentConfig", {
                beta: true, // 必须这么写,否则wx.invoke调用形式的jsapi会有问题
                debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                agentid: resData[0].agentid, // 必填,单位应用的agentid
                corpid: resData[0].corpid,  // 必填,政务微信的corpid
                timestamp: this.res.timestamp, // 必填,生成签名的时间戳,int类型, 如 1539100800
                nonceStr: this.res.noncestr, // 必填,生成签名的随机串
                signature: this.res.agentSignature,// 必填,签名,见附录5
                jsApiList: ["openUserProfile","getStepCount","addCalendarEvent","getAllPhoneContacts","selectEnterpriseContact"] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
            }