# 企业微信 OAuth 2.0 后端实现指南 ## 概述 这个文档提供企业微信 OAuth 2.0 认证的后端实现指南。前端会将授权码发送到后端,后端使用该授权码与企业微信服务器交互获取用户信息。 ## 前置条件 1. 已在企业微信管理后台创建应用 2. 获取到企业 ID(CorpID)和应用 Secret 3. 在应用配置中设置回调 URL ## 流程图 ``` ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ 前端页面 │ │ 企业微信 │ │ 后端服务 │ │ │ │ OAuth 服务 │ │ │ └──────┬──────┘ └──────────────┘ └──────────────┘ │ │ │ │ 1. 获取授权码 │ │ ├──────────────────────>│ │ │ (重定向到授权页面) │ │ │ │ │ │ 2. 用户授权 │ │ │ (返回授权码和code) │ │ │<──────────────────────┤ │ │ │ │ │ 3. 发送授权码到后端 │ │ ├─────────────────────────────────────────────────>│ │ (POST /api/wecom/getUserInfo) │ │ │ │ │ │ 4. 使用 code 和 secret │ │ │ 交换 access_token │ │ │<────────────────────────┤ │ │ │ │ 5. 返回用户信息 │ │ │<─────────────────────────────────────────────────┤ │ │ │ ``` ## 后端 API 端点实现 ### 1. 获取用户信息端点 **请求** ``` POST /api/wecom/getUserInfo Content-Type: application/json { "code": "获取的授权码" } ``` **响应** ```json { "code": 0, "message": "success", "data": { "userid": "user123", "name": "张三", "mobile": "13800138000", "email": "zhang@company.com", "department": "技术部", "position": "工程师" } } ``` ### 2. 验证用户权限端点 **请求** ``` GET /api/wecom/verify/:userId ``` **响应** ```json { "code": 0, "message": "success", "data": { "authorized": true } } ``` ## Node.js + Express 实现示例 ```javascript // 需要安装: npm install axios dotenv const express = require('express'); const axios = require('axios'); require('dotenv').config(); const router = express.Router(); // 获取访问令牌 async function getAccessToken() { try { const response = await axios.get( 'https://qyapi.weixin.qq.com/cgi-bin/gettoken', { params: { corpid: process.env.WECOM_CORP_ID, corpsecret: process.env.WECOM_APP_SECRET } } ); return response.data.access_token; } catch (error) { console.error('获取 access_token 失败:', error); throw error; } } // 使用授权码获取用户信息 router.post('/wecom/getUserInfo', async (req, res) => { const { code } = req.body; if (!code) { return res.status(400).json({ code: -1, message: '缺少授权码' }); } try { const accessToken = await getAccessToken(); // 使用授权码获取用户身份信息 const userResponse = await axios.get( 'https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail', { params: { access_token: accessToken, code: code } } ); if (userResponse.data.errcode === 0) { const userInfo = { userid: userResponse.data.userid, name: userResponse.data.name, mobile: userResponse.data.mobile, email: userResponse.data.email, department: userResponse.data.department?.[0] || '', position: userResponse.data.position }; // 可以在这里存储用户信息到数据库 res.json({ code: 0, message: 'success', data: userInfo }); } else { res.status(401).json({ code: userResponse.data.errcode, message: userResponse.data.errmsg }); } } catch (error) { console.error('获取用户信息失败:', error); res.status(500).json({ code: -1, message: '获取用户信息失败' }); } }); // 验证用户权限 router.get('/wecom/verify/:userId', async (req, res) => { const { userId } = req.params; try { // 实现你的权限验证逻辑 const authorized = await checkUserPermission(userId); res.json({ code: 0, message: 'success', data: { authorized: authorized } }); } catch (error) { console.error('验证用户权限失败:', error); res.status(500).json({ code: -1, message: '验证失败' }); } }); module.exports = router; ``` ## 环境变量配置 (.env) ```env # 企业微信配置 WECOM_CORP_ID=your_corp_id WECOM_APP_SECRET=your_app_secret WECOM_AGENT_ID=your_agent_id # 应用配置 NODE_ENV=production PORT=3000 ``` ## Python Flask 实现示例 ```python from flask import Blueprint, request, jsonify import requests import os wecom_bp = Blueprint('wecom', __name__, url_prefix='/api') def get_access_token(): """获取企业微信访问令牌""" url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken' params = { 'corpid': os.getenv('WECOM_CORP_ID'), 'corpsecret': os.getenv('WECOM_APP_SECRET') } response = requests.get(url, params=params) data = response.json() return data.get('access_token') @wecom_bp.route('/wecom/getUserInfo', methods=['POST']) def get_user_info(): """使用授权码获取用户信息""" code = request.json.get('code') if not code: return jsonify({ 'code': -1, 'message': '缺少授权码' }), 400 try: access_token = get_access_token() # 获取用户身份信息 url = 'https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail' params = { 'access_token': access_token, 'code': code } response = requests.get(url, params=params) data = response.json() if data.get('errcode') == 0: user_info = { 'userid': data.get('userid'), 'name': data.get('name'), 'mobile': data.get('mobile'), 'email': data.get('email'), 'department': data.get('department', [])[0] if data.get('department') else '', 'position': data.get('position') } return jsonify({ 'code': 0, 'message': 'success', 'data': user_info }) else: return jsonify({ 'code': data.get('errcode'), 'message': data.get('errmsg') }), 401 except Exception as e: return jsonify({ 'code': -1, 'message': f'获取用户信息失败: {str(e)}' }), 500 @wecom_bp.route('/wecom/verify/', methods=['GET']) def verify_user(user_id): """验证用户权限""" try: # 实现你的权限验证逻辑 authorized = check_user_permission(user_id) return jsonify({ 'code': 0, 'message': 'success', 'data': { 'authorized': authorized } }) except Exception as e: return jsonify({ 'code': -1, 'message': '验证失败' }), 500 ``` ## 关键说明 ### 1. 安全建议 - ✅ 使用 HTTPS 传输所有数据 - ✅ 后端必须验证 state 参数防止 CSRF 攻击 - ✅ 不要在前端暴露 Secret - ✅ 使用 Session 或 Token 维护用户状态 - ✅ 实现请求签名验证 ### 2. 错误处理 常见错误码: - `40001`: access_token 过期 - `40013`: 非法的 secret - `40014`: 无效的用户ID - `82001`: 参数错误 ### 3. 接收消息验证 企业微信可能会发送消息给应用,需要验证签名: ```javascript function verifySignature(timestamp, nonce, msgEncrypt, token, encodingAESKey) { // 实现签名验证逻辑 // 按照文档进行 SHA1 校验 } ``` ## 企业微信管理后台配置 1. 登录 [企业微信管理后台](https://work.weixin.qq.com/) 2. 进入应用管理 > 创建应用 3. 在应用详情中设置: - 应用名称 - 应用 Logo - 可见范围(选择哪些部门可以看到) - 回调 URL(重定向地址) 4. 获取应用的 AgentID 和 Secret ## 测试 使用 curl 测试 API: ```bash # 获取用户信息 curl -X POST http://localhost:3000/api/wecom/getUserInfo \ -H "Content-Type: application/json" \ -d '{"code": "your_auth_code"}' # 验证用户权限 curl http://localhost:3000/api/wecom/verify/user123 ``` ## 参考文档 - [企业微信官方文档](https://work.weixin.qq.com/api/doc/90000/90135/90202) - [OAuth 2.0 标准](https://oauth.net/2/) - [企业微信 SDK](https://github.com/wechatpay-apiv3/wechatpay-go) ## 常见问题 **Q: 如何处理 Token 过期?** A: 实现 Token 刷新机制,当收到 40001 错误时重新获取 access_token。 **Q: 如何限制访问范围?** A: 在后端验证用户身份后,根据用户的部门和角色进行权限判断。 **Q: 可以获取用户哪些信息?** A: 根据授权范围(scope)和应用权限,可获取用户ID、名称、手机、邮箱、部门等。