开始前
第一步:需要在相应的企业微信私有化的管理端新建一个应用
第二步:在自建应用里面配置好可信域名:open.gov.weixin.qq.com (你自己服务器的域名)
配置好这两步之后就可以进行后端签名了
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 & 0x1F), 0x80 + (c & 0x3F));
else {
if ((x = c ^ 0xD800) >> 10 == 0) //对四字节UTF-16转换为Unicode
c = (x << 10) + (s.charCodeAt(++i) ^ 0xDC00) + 0x10000,
r.push(0xF0 + (c >> 18 & 0x7), 0x80 + (c >> 12 & 0x3F));
else r.push(0xE0 + (c >> 12 & 0xF));
r.push(0x80 + (c >> 6 & 0x3F), 0x80 + (c & 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 & 3) * 8);
s[l - 1] = data.length << 3;
var w = [],
f = [
function() {
return m[1] & m[2] | ~m[1] & m[3];
},
function() {
return m[1] ^ m[2] ^ m[3];
},
function() {
return m[1] & m[2] | m[1] & m[3] | m[2] & 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
}