feat:增加企业微信接口博客

This commit is contained in:
2026-01-23 17:08:40 +08:00
parent 2549b696e1
commit b39d345da9
2 changed files with 452 additions and 0 deletions

451
docs/Others/WeCom.md Normal file
View File

@@ -0,0 +1,451 @@
---
title: 企业微信工作台开发
date: 2026-01-23
---
  接口文档地址:[接口](https://developer.work.weixin.qq.com/document/path/90664)
# 一、建立工作台
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(`
<html>
<body>
<h1>登录失败</h1>
<p>${error.message}</p>
</body>
</html>
`);
}
}
// 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}&timestamp=${timestamp}&url=${url}`;
return crypto.createHash('sha1').update(string).digest('hex');
}
module.exports = {
generateNonceStr,
generateSignature
};
```
# 四、应用主页
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>企业微信登录跳转</title>
<!-- 引入新版企业微信JS-SDK -->
<script src="https://wwcdn.weixin.qq.com/node/open/js/wecom-jssdk-2.3.3.js"></script>
</head>
<body>
<div id="loading">正在跳转,请稍候...</div>
<script>
// 配置参数
const serverUrl = ''
const config = {
corpId: '', // 企业ID
agentId: '', // 应用ID
redirectUrl: encodeURIComponent(`${serverUrl}auth/callback/repair`), // OAuth回调地址
authUrl: `${serverUrl}auth/config/repair` // 后端授权入口
};
// 主函数
async function init() {
try {
// 获取当前页面URL用于签名
const currentUrl = window.location.href.split('#')[0];
// 从后端获取签名信息
const signatureData = await getSignatureData(currentUrl);
const getConfigSignature = function(url) {
return {
timestamp: signatureData.timestamp,
nonceStr: signatureData.nonceStr,
signature: signatureData.signature
};
};
// 使用ww.register 进行身份注册
await ww.register({
corpId: config.corpId,
agentId: config.agentId,
jsApiList: ['openDefaultBrowser', 'closeWindow'],
getConfigSignature: getConfigSignature
});
// 生成授权URL
const authUrl = await generateAuthUrl();
const isMobile = /(phone|pad|pod|iphone|ipod|ios|ipad|android|mobile|blackberry|iemobile|mqqbrowser|fennec|wosbrowser|browserng|webos|symbian|windows phone)/i.test(navigator.userAgent);
if (isMobile) {
// 在移动端企业微信中直接打开链接
window.location.href = authUrl;
} else {
// 使用新版API调用系统浏览器
ww.openDefaultBrowser({
url: authUrl,
success: (res) => {
// console.log('成功调用系统浏览器:', res);
ww.closeWindow({})
},
fail: (err) => {
// console.error('打开浏览器失败:', err);
document.getElementById('loading').innerHTML = `打开失败: ${err.errMsg || '未知错误'},请点击右上角菜单选择"在浏览器中打开"`;
}
});
}
} catch (error) {
console.error('初始化失败:', error);
document.getElementById('loading').innerHTML = '跳转失败,请手动在浏览器中打开: ' + error.errMsg;
// window.alert(error.errMsg)
}
}
// 获取签名数据
async function getSignatureData(url) {
const response = await fetch(`${config.authUrl}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: url
})
});
if (!response.ok) {
throw new Error('获取签名配置失败');
}
const result = await response.json();
// 确保返回的数据包含必要的字段
if (!result.timestamp || !result.nonceStr || !result.signature) {
throw new Error('签名数据不完整');
}
return result;
}
// 生成授权URL
async function generateAuthUrl() {
// 使用URLSearchParams构建参数
const authParams = new URLSearchParams({
appid: config.corpId,
redirect_uri: config.redirectUrl,
response_type: 'code',
scope: 'snsapi_base', // 静默授权
state: 'wecom_redirect',
agentid: config.agentId
});
return `https://open.weixin.qq.com/connect/oauth2/authorize?${authParams.toString()}#wechat_redirect`;
}
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>
```
&emsp;&emsp;将该应用主页的URL放在工作台的应用主页中。
::: tip
需要在前端工程中配置路由参数访问即通过ip:port/#/login?username=''&password=''的形式访问。
:::