背景
企业微信应用开发分为两种开发方式:
- 服务商代开发应用
- 企业自建应用
使用服务商代开发模式,由于代开发应用,企业微信需要认证,无法调用企业微信sdk; 而企业自建应用,无需进行认证,可以调用企微提供的开放能力。
本文主要内容是前端如何搭建一个企业微信自建应用的开发环境:
- 创建一个自建应用
- 介绍企业微信的授权流程
- 前端调用流程和后端调用企微的流程
- 搭建过程中的一些报错
1. 创建企微自建应用
1.1 新建应用
在企业微信后台下->应用管理->应用->创建应用


此处需要记住 AgentId,其中Secret前端不需要。

这样一个自建应用就创建完成了。
下面介绍下配置到聊天附件栏、和聊天工具栏的配置和表现形式。
1.2 聊天工具栏
从上面页面下可以点击配置到聊天工具栏,进行配置链接,此处的链接可以参考我上一篇文章企业微信侧边栏本地开发调试


企业微信中表现形式:

1.3 聊天附件栏
从上面页面下可以点击配置到聊天附件栏,进行配置链接,同上。
企业微信中表现形式:


2. 企微授权流程梳理
我们先看一下企业微信整体的鉴权流程,如下图:

前后端调用具体的流程图如下:

接下来我们需要搭建创建一个前端项目和一个node服务(这里使用nestjs)。
3. 前、后端调用流程
这里我们主要分为两个部分,一部分是前端代码,一部分是后端代码。
3.1 前端代码部分
具体步骤参考企业微信JS-SDK开发文档,我这里只做重要的讲解。
3.1.1 引入企微相关sdk
js
<!-- 需要调用JS接口的页面引入如下JS文件 z -->
<script src="https://res.wx.qq.com/open/js/jweixin-1.2.0.js" type="text/javascript"></script>
<!--调用 wx.agentConfig需要引入 jwxwork sdk-->
<script src="https://open.work.weixin.qq.com/wwopen/js/jwxwork-1.0.0.js"></script>
3.1.2 通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用)。
js
wx.config({
beta: true,// 必须这么写,否则wx.invoke调用形式的jsapi会有问题
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: '', // 必填,企业微信的corpID
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
注意:此处timestamp、nonceStr、signature由后端提供,其余参数均可知
3.1.3 agentConfig注入应用的权限
在ready后调用agentConfig注入实际开发中需要的权限,例如转发、分享等。
js
wx.ready(function(){
wx.agentConfig({
corpid: '', // 必填,企业微信的corpid,必须与当前登录的企业一致
agentid: '', // 必填,企业微信的应用id
timestamp: , // 必填,生成签名的时间戳
nonceStr: '', // 必填,生成签名的随机串
signature: '',// 必填,签名,见附录1
jsApiList: [], //必填
success: function(res) {
// 回调
},
fail: function(res) {
if(res.errMsg.indexOf('function not exist') > -1){
alert('版本过低请升级')
}
}
});
});
注意:此处timestamp、nonceStr、signature由后端提供,还有一点需要注意的是wx.config和wx.agentConfig需要后端的提供的数据只是参数名相同,内容是不同的。
3.1.4 判断是否授权
首先在进入页面时会先判断当前url 是否有code 参数,如果有code则走登录逻辑(企业微信侧边栏一般默认都是静默登录),否则就跳转企业微信授权页面。
如果没有前端代码如下:
js
https://open.weixin.qq.com/connect/oauth2/authorize?appid=CORPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE&agentid=AGENTID#wechat_redirect
无授权文档说明,进行跳转授权,参数说明如下:
如果有code则需要后端获取访问用户身份。
下面我们需要搭建一个后端服务,去调用企业微信的接口。
3.2 搭建后端服务
主要功能是用于自身开发服务器向企微服务请求ticket,并将签名信息返回给前端。
最主要的流程是:
3.2.1 通过corpid, corpsecret获取access_token
js
/** 获取企业微信jsapi的ticket */
async getAccessToken(query) {
try {
const { corpid, corpsecret } = query
const res = await firstValueFrom(
this.httpService.get(`https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=${corpid}&corpsecret=${corpsecret}`)
)
if (res.data.errcode !== 0) {
throw new HttpException("获取失败", HttpStatus.OK);
}
return res.data.access_token
} catch (error) {
handleError(this.logger, error, {
common: "获取企业微信Access Token失败",
});
}
}
3.2.2 调用企微接口get_jsapi_ticket
获取ticket, 用于wx.config签名返回
获取到ticket进行签名,返回给前端wx.config的三个参数
js
/** 获取企业微信config的ticket 用于config */
async getConfigTicket(accessToken: string) {
try {
const res = await firstValueFrom(
this.httpService.get(`https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket?access_token=${accessToken}`)
)
return res.data.ticket
} catch (error) {
handleError(this.logger, error, {
common: "获取企业微信config的ticket失败",
});
}
}
签名:
js
/**
* 生成随机字符串
* @param length 字符串长度,默认16
* @returns 随机字符串
*/
private generateNonceStr(length = 16): string {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
}
}
/**
* 生成微信JS-SDK签名
* @param jsapi_ticket 微信JSAPI Ticket
* @param url 当前网页URL(不包含#及其后面部分)
* @returns 返回签名所需的所有参数
*/
async generateQWSignature(query) {
const { url, type, corpid, corpsecret } = query
const token = await this.getAccessToken({ corpid, corpsecret })
// 0. 获取jsapi_ticket
const ticket = type === 'config' ? await this.getConfigTicket(token) : await this.getAgentConfigTicket(token)
// 1. 生成随机字符串(16位)
const noncestr = this.generateNonceStr();
// 2. 生成时间戳(秒级)
const timestamp = Math.floor(Date.now() / 1000);
// 3. 按固定顺序拼接字符串
const string1 = `jsapi_ticket=${ticket}&noncestr=${noncestr}×tamp=${timestamp}&url=${url}`;
// 4. 生成SHA1签名
const signature = createHash('sha1')
.update(string1)
.digest('hex');
return {
noncestr,
timestamp,
url,
signature,
};
}
3.2.3 调用企微接口get
获取ticket, 用于wx.agentConfig签名返回
js
/** 获取企业微信agent_config的 jsapi的ticket 用于接口权限 */
async getAgentConfigTicket(accessToken: string) {
try {
const res = await firstValueFrom(
this.httpService.get(`https://qyapi.weixin.qq.com/cgi-bin/ticket/get?access_token=${accessToken}&type=agent_config`)
)
return res.data.ticket
} catch (error) {
handleError(this.logger, error, {
common: "获取企业微信agent_config的ticket失败",
});
}
}
签名函数同上,区分类型即可。
4. 搭建过程中的问题
- 获取wx.config和wx.agentConfig的ticket弄混,调用的接口是不一样的
- 接口访问需要添加ip白名单
- 报错40093,签名的url需要添加/
- 要用应用的Secret获取access_token来获取jsapi_ticket和agent jsapi_ticket,不要用企业的corpSecret
5. 总结
最后总结一下,搭建一个企业微信的应用开发环境,需要理解企业微信授权的整体流程,过程中也遇到了一些问题,总结下希望能对大家有一点点的帮助。如有错误,请指正O^O!