一、流程
1. 前端 => 获取code
前端鉴权,拿到code后,传递给后端
2. 后端 => 获取access_token
其它的业务API接口,都需要依赖于access_token来鉴权调用者身份
3. 后端 => 获取企业的jsapi_ticket
进行签名算法,生成signature wx.config和wx.agentConfig 签名不可共用
4. 前端 => 获得权限配置
前端请求接口,拿到数据后进行wx.config以及wx.agentConfig配置
5. 前端 => 获取外部联系人userId
6. 后端 => 获取用户的unionid
7. 后端 => 获取用户基本信息
前端把code传递给后端后,后端通过code和access_token获取数据
ps : 一般这里后端可以返回token给前端,前端后续的请求是携带即可
unionid: 外部联系人在微信开放平台的唯一身份标识(微信unionid),通过此字段企业可将外部联系人与公众号/小程序用户关联起来。仅当联系人类型是微信用户,且企业或第三方服务商绑定了微信开发者ID有此字段。
通过获取的 unionid 与 公司后台已存在的 unionid 匹配,从而获取该外部联系人在 公司后台 的基本信息,返回到前端进行展示
二、实际操作
1. 引入js文件
// 在index.html中,加入
<script type="text/javascript" src="//res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
2. App.vue
企业微信侧边栏一般潜入h5页面,这里做个页面配置,限制宽度等
<template>
<div id="app" :class="{'fixed-app': fixedStatus}">
<router-view/>
</div>
</template>
<script>
export default {
data() {
return {
// 是否固定app宽度
fixedStatus: false
};
},
created() {
this.resize();
window.addEventListener('resize', this.resize, false);
},
beforeDestroy() {
window.removeEventListener('resize', this.resize, false);
},
methods: {
resize() {
if(/Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent)) { // 移动端
// 移动端操作
this.fixedStatus = false;
} else {
// pc端操作
// 固定app宽度
this.fixedStatus = true;
}
}
}
};
</script>
<style lang="scss">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
font-size: .14rem;
&.fixed-app {
max-width: 750px;
min-width: 320px;
margin: 0 auto;
box-sizing: border-box;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Roboto","Oxygen","Ubuntu","Cantarell","Fira Sans","Droid Sans","Helvetica Neue","Microsoft Yahei",sans-serif;
overflow: hidden;
}
}
</style>
3. 配置可信域名
现在企业微信更新后,只能访问外网地址,本地调试时先跳过code授权之类的
等测试时,再进行授权调试
配置可信域名:应用管理 -> 点击创建的应用 -> 网页授权及 JS-SDK
4. 获取code
构造网页授权链接,获取code : 构造网页授权链接
window.location.href = 链接即可。如果请求成功,code会显示在浏览器地址上
getWeixinGetAuthorityUrl() {
// 配置参数 => 可以直接写死,也可以通过接口获取
const opt = {
appid: 'xxx', // 企业微信的appid
redirect_uri: encodeURIComponent(window.location.href), // 重定向地址,需外网地址,需要进行UrlEncode
response_type: 'code', // 返回类型,固定为:code
scope: 'snsapi_base' // 静默授权,可获取成员的基础信息(UserId与DeviceId)
};
const str = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${opt.appid}&redirect_uri=${opt.redirect_uri}&response_type=${opt.response_type}&scope=${opt.scope}&state=weixin#wechat_redirect`;
// 重新跳转到当前位置
/** 当然会出现请在企业微信客户端打开链接,只能配置外网环境,在里面进行测试了,但是成功后,地址栏后面就有code参数 */
window.location.href = str;
}
5. 注入权限验证配置
方式一
直接写在页面中进行配置
window.wx.config({
// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
beta: true,
// 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印
debug: true,
// 必填,企业微信的corpID,必须是本企业的corpID,不允许跨企业使用
appId: 'xxx',
// 必填,生成签名的时间戳
timestamp: '1204314209',
// 必填,生成签名的随机串
nonceStr: 'L12kHl4M4l1BFNs2',
// 必填,签名,见 附录-JS-SDK使用权限签名算法
signature: '334bgd6d78d50agds552fab434bb39b780fb008e',
// 必填,需要使用的JS接口列表,凡是要调用的接口都需要传进来
jsApiList: ["getCurExternalContact", "openUserProfile", "hideOptionMenu", "agentConfig"]
});
方式二
通过接口让后端返回
ps : 这里可以利用特性
script标签的onload事件都是在外部js文件被加载完成并执行完成后才被触发的
// 1. 获得head元素
const headDom = document.querySelector('head');
// 2. 创建script元素
const scriptDom = document.createElement('script');
// 3. 设置script元素的属性
scriptDom.type = 'text/javascript';
/**
* 4. 监听script元素的加载事件,加载完成后执行回调
* 这里已经执行完成了scriptDom,也就是权限已经注入了,但是还没有执行wx.ready
*/
scriptDom.onload = scriptDom.onreadystatechange = function() {};
/**
* 5. 设置script元素的src属性
* 01. 获取当前页面的url => (不包含#及其后面部分)
* 例如:http://localhost:8080/ins-web/#/home => http://localhost:8080/ins-web
* 02. 将url传给后端,后端根据url生成签名 => (后端需要提供一个接口,接收url参数,返回签名)
* 03. 将签名返回给前端 => (后端接口返回的数据结构为:{appId: '', timestamp: '', nonceStr: '', signature: ''})
*/
const locationUrl = encodeURIComponent(window.location.href.split('#')[0]);
const url = `/ins-api/wechat/jsapi/signature?url=${locationUrl}`;
scriptDom.src = url;
// https://xxx.com//ins-api/wechat/jsapi/signature?url=https%3A%2F%2Fxxx.com%2F%3Fcode%3DlVmYFNlGQTrRQsBg1Cm1ngSDSIlSr5BQzgai4ZDbW5k%26state%3Dweixin
// 6. 将script元素添加到head元素中,此时浏览器会自动加载script元素,执行其中的代码,从而注入权限
headDom.appendChild(scriptDom);
6. 注入应用的权限
wx.agentConfig({
// 必填,企业微信的corpid,必须与当前登录的企业一致
corpid: 'xxx',
// 必填,企业微信的应用id (e.g. 1000247)
agentid: 'xxx',
// 必填,生成签名的时间戳
timestamp: '1421314709',
// 必填,生成签名的随机串
nonceStr: 'xxx',
// 必填,签名,见附录-JS-SDK使用权限签名算法
signature: 'xxx',
//必填,传入需要使用的接口名称
jsApiList: ["getCurExternalContact", "openUserProfile", "hideOptionMenu", "openEnterpriseChat"],
// 成功的回调
success: function(res) {
},
// 失败的回调
fail: function(res) {
if(res.errMsg.indexOf('function not exist') > -1){
alert('版本过低请升级')
}
}
});
方式一
如果权限是通过方式一获取的
//注入权限验证配置
window.wx.config({
beta: true,
debug: true,
appId: '',
timestamp: '',
nonceStr: '',
signature: '',
jsApiList: []
});
// 注入权限验证配置
setTimeout(()=>{
window.wx.ready(() => {
// 请求获取应用权限
axios.get('balabalabala').then(cof => {
const confObj = cof.data.data || {};
// 注入应用的权限
window.wx.agentConfig({
// 通过后端接口直接获取也可
...confObj,
success: function(res) {},
fail: function(res) {}
});
})
});
},1000)
方式二
如果权限是通过方式二获取的,可以在onload中继续操作
const headDom = document.querySelector('head');
const scriptDom = document.createElement('script');
scriptDom.type = 'text/javascript';
scriptDom.onload = scriptDom.onreadystatechange = function() {
/**
* readyState 表示当前状态,loaded 表示已经加载完成,complete 表示已经执行完成
* 这里的this指向的是script元素
*/
if (!this.readyState || this.readyState === 'loaded' || this.readyState === 'complete') {
window.wx.ready(() => {
// 注入应用的权限
ajax.get('balabalabala').then(cof => {
const confObj = cof.data.data || {};
window.wx.agentConfig({
...confObj,
success: function(res) {},
fail: function(res) {}
});
});
});
// Handle memory leak in IE => 释放内存,防止内存泄漏
script.onload = script.onreadystatechange = null;
}
};
const locationUrl = encodeURIComponent(window.location.href.split('#')[0]);
const url = `/ins-api/wechat/jsapi/signature?url=${locationUrl}`;
scriptDom.src = url;
headDom.appendChild(scriptDom);
7. 获取外部联系人userId
ps : 需要在权限中的jsApiList配置getCurExternalContact
wx.invoke('getCurExternalContact', {}, function(res){
if(res.err_msg == "getCurExternalContact:ok"){
userId = res.userId ; //返回当前外部联系人userId
}else {
//错误处理
}
});
不过需要在注入应用权限的成功回调中进行操作
window.wx.agentConfig({
...confObj,
success: function(res) {
// 获取外部联系人userId
window.wx.invoke('getCurExternalContact', {}, function(res) {
if (res.err_msg == 'getCurExternalContact:ok') {
// 返回当前外部联系人userId
const userId = res.userId;
} else {
console.log('获取外部联系人userId失败');
}
});
},
fail: function(res) {
console.log('agentConfig fail', res);
}
});
8. 获取用户信息
通过传入code和外部联系人userid给后端
getInsToken() {
if (!this.userId) {
this.$message({
msg: '获取当前外部联系人id失败'
});
return false;
}
const response = await axios.get('xxx', {
params: {
// 地址栏获取的code
code: this.code,
// 外部联系人id
userId: this.userId
}
});
const data = response.data.data || {};
if (!data.token) {
this.description = '请先在后台注册账号';
return false;
}
window.localStorage.setItem('SESSIONID', data.token);
window.localStorage.setItem('customerId', data.customerId);
window.localStorage.setItem('externalUserId', this.userId);
window.localStorage.setItem('userName', data.userName);
}