--- title: 企业微信工作台开发 date: 2026-01-23 --- # 一、建立工作台 1. 进入企业微信后台管理系统,选择应用管理->应用管理。 2. 选择自建->创建应用。 3. 输入名称、图标等信息,分配可见范围,完成应用创建. 4. 点击应用,选择开发者接口->网页授权,配置可信域名和企业可信IP。 5. 查看并保存AgentId和Secret。 ::: tip 可信域名不能是IP形式,必须是和企业微信备案主体一致的服务器。 ::: # 二、JS-SDK开发 ## 2.1 接口鉴权   [官方文档](https://developer.work.weixin.qq.com/document/path/90514)   参考第三节企业微信鉴权接口开发 ## 2.2 打开默认浏览器   使用系统浏览器打开指定 URL,支持传入 oauth2 链接,从而实现在系统浏览器内免登录的效果。 # 三、鉴权接口开发   本教程以node.js为例。 ## 3.1 通用企业微信服务端API ```js const axios = require('axios'); const TOKEN_URL = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken' const JSAPI_TICKET_URL = 'https://qyapi.weixin.qq.com/cgi-bin/get_jsapi_ticket' const MESSAGE_URL = 'https://qyapi.weixin.qq.com/cgi-bin/message/send' // 获取access_token async function getAccessTokenApi(corpId, corpSecret) { const now = Date.now(); try { const url = `${TOKEN_URL}?corpid=${corpId}&corpsecret=${corpSecret}` const response = await axios.get(url); if (response.data.errcode === 0) { return { accessToken: response.data.access_token, expires: now + (response.data.expires_in - 300) * 1000 // 提前5分钟过期 }; } else { throw new Error(`获取access_token失败: ${response.data.errmsg}`); } } catch (error) { console.error('获取access_token错误:', error); throw error; } } // 获取jsapi_ticket async function getJsapiTicketApi(accessToken) { try { const url = `${JSAPI_TICKET_URL}?access_token=${accessToken}` const response = await axios.get(url); if (response.data.errcode === 0) { return response.data.ticket; } else { throw new Error(`获取jsapi_ticket失败: ${response.data.errmsg}`); } } catch (error) { console.error('获取jsapi_ticket错误:', error); throw error; } } // 发送应用消息 async function sendMessageApi(agentid, accessToken, touser, content) { try { const url = `${MESSAGE_URL}?access_token=${accessToken}` const response = await axios.post(url, { touser: touser, msgtype: 'text', agentid: agentid, text: { content: content } }); if (response.data.errcode === 0) { return true; } else { throw new Error(`发送应用消息失败: ${response.data.errmsg}`); } } catch (error) { console.error('发送应用消息错误:', error); throw error; } } module.exports = { getAccessTokenApi, getJsapiTicketApi, sendMessageApi }; ``` ## 3.2 自建应用鉴权接口 ```js const {getAccessTokenApi, getJsapiTicketApi, sendMessageApi} = require("./wecom"); const config = { corpId: '', // 企业id corpSecret: '', // 应用密码 agentId: '', // 应用id domain: '', // 跳转域名 password: '' // 登录默认密码 }; // 存储access_token和jsapi_ticket let tokenCache = { accessToken: null, jsapiTicket: null, expires: 0 }; // 获取access_token async function getRepairAccessToken() { const now = Date.now(); if (tokenCache.accessToken && tokenCache.expires > now) { return tokenCache.accessToken; } const result = await getAccessTokenApi(config.corpId, config.corpSecret) tokenCache.accessToken = result.accessToken; tokenCache.expires = result.expires } // 获取jsapi_ticket async function getRepairJsapiTicket() { const now = Date.now(); if (tokenCache.jsapiTicket && tokenCache.expires > now) { return tokenCache.jsapiTicket; } await getRepairAccessToken(); tokenCache.jsapiTicket = await getJsapiTicketApi(tokenCache.accessToken); } // 发送应用消息 async function sendRepairMessage(touser, content) { await getRepairAccessToken(); await sendMessageApi(config.agentId, tokenCache.accessToken, touser, content); } function getRepairDomain() { return config.domain } function getRepairPassword() { return config.password } module.exports = { getRepairAccessToken, getRepairJsapiTicket, sendRepairMessage, getRepairDomain, getRepairPassword }; ``` ## 3.3 NodeJS API ```js const express = require('express'); const axios = require('axios'); const cors = require('cors'); const {generateNonceStr, generateSignature} = require("./utils"); const {sendRepairMessage, getRepairJsapiTicket, getRepairAccessToken, getRepairDomain, getRepairPassword} = require("./repair"); const app = express(); const PORT = 3000; app.use(cors()); app.use(express.json()); const corpId = 'ww48932728388c9e3a' const USER_INFO_URL = 'https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo' // API接口:生成JS-SDK配置 app.post('/auth/config/repair', async (req, res) => { await configService(req, res, 'repair') }); async function configService(req, res, appName) { try { const {url} = req.body; if (!url) { return res.status(400).json({errcode: -1, errmsg: '缺少url参数'}); } let jsapiTicket = '' if (appName === 'repair') { jsapiTicket = await getRepairJsapiTicket(); } const timestamp = Math.floor(Date.now() / 1000); const noncestr = generateNonceStr(); const signature = generateSignature(jsapiTicket, noncestr, timestamp, url); res.json({ errcode: 0, errmsg: 'ok', appId: corpId, timestamp, nonceStr: noncestr, signature }); } catch (error) { console.error('生成JS-SDK配置错误:', error); res.status(500).json({errcode: -1, errmsg: '服务器内部错误'}); } } // API接口:OAuth回调处理 app.get('/auth/callback/repair', async (req, res) => { await callback(req, res, 'repair') }); async function callback(req, res, appName) { try { const {code, state} = req.query; if (!code) { return res.status(400).send('缺少code参数'); } // 通过code获取用户信息 let accessToken = '' if (appName === 'repair') { accessToken = await getRepairAccessToken(); } const url = `${USER_INFO_URL}?access_token=${accessToken}&code=${code}` const userResponse = await axios.get(url); if (userResponse.data.errcode !== 0) { throw new Error(`获取用户信息失败: ${userResponse.data.errmsg}`); } const userInfo = userResponse.data; // 重定向到前端页面 let redirectUrl = '' if (appName === 'repair') { redirectUrl = `${getRepairDomain()}?username=${userInfo.UserId}&password=${getRepairPassword()}&type=repair`; } res.redirect(redirectUrl); } catch (error) { console.error('OAuth回调处理错误:', error); res.status(500).send(`

登录失败

${error.message}

`); } } // API接口:发现应用消息 app.post('/message/repair', async (req, res) => { const {touser, content} = req.body; await sendRepairMessage(touser, content) res.json({ errcode: 0, errmsg: 'ok' }); }); // 启动服务 app.listen(PORT, () => { console.log(`企业微信登录服务运行在端口 ${PORT}`); }); module.exports = app; ``` ## 3.4 工具类 ```js const crypto = require('crypto'); // 工具函数:生成随机字符串 function generateNonceStr(length = 16) { const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; let result = ''; for (let i = 0; i < length; i++) { result += chars.charAt(Math.floor(Math.random() * chars.length)); } return result; } // 工具函数:生成签名 function generateSignature(jsapiTicket, noncestr, timestamp, url) { const string = `jsapi_ticket=${jsapiTicket}&noncestr=${noncestr}×tamp=${timestamp}&url=${url}`; return crypto.createHash('sha1').update(string).digest('hex'); } module.exports = { generateNonceStr, generateSignature }; ``` # 四、应用主页 ```html 企业微信登录跳转
正在跳转,请稍候...
```   将该应用主页的URL放在工作台的应用主页中。 ::: tip 需要在前端工程中配置路由参数访问,即通过ip:port/#/login?username=''&password=''的形式访问。 ::: ::: tip 如果需要在发送应用消息时,可以点击消息访问应用,绑定的URL链接也是该应用主页的链接。 ::: # 五、使用   本教程以SpringBoot为例。 ## 5.1 配置RestClient ```java @Configuration public class RestClientConfig { final String weiComBaseUrl = ""; @Bean public RestClient weiComClient() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); factory.setConnectTimeout(Duration.ofSeconds(5)); factory.setReadTimeout(Duration.ofSeconds(10)); return RestClient.builder() .requestFactory(factory) .baseUrl(weiComBaseUrl) .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE) .build(); } } ``` ## 5.2 调用发送消息接口 ```java @Service @Slf4j public class NoticeService { @Resource private RestClient weiComClient; @Async public void sendRepairMessage(String touser, String content) { String link = ""; WeComNoticeRequest request = new WeComNoticeRequest(); request.setTouser(touser); request.setContent(content + "\n点击打开工作台应用:" + "打开应用"); WeComResponse response = weiComClient.post() .uri("/message/repair") .contentType(MediaType.APPLICATION_JSON) .body(request) .retrieve() .body(WeComResponse.class); if (response == null || response.getErrcode() != 0) { throw new CustomException("企业微信发送消息失败 请联系管理员"); } } } ``` # 附录 企业微信官方文档:[接口](https://developer.work.weixin.qq.com/document/path/90664)