编辑 | blame | 历史 | 原始文档

企业微信 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 实现示例

// 需要安装: 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)

# 企业微信配置
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 实现示例

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/<user_id>', 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. 接收消息验证

企业微信可能会发送消息给应用,需要验证签名:

function verifySignature(timestamp, nonce, msgEncrypt, token, encodingAESKey) {
    // 实现签名验证逻辑
    // 按照文档进行 SHA1 校验
}

企业微信管理后台配置

  1. 登录 企业微信管理后台
  2. 进入应用管理 > 创建应用
  3. 在应用详情中设置:
  • 应用名称
  • 应用 Logo
  • 可见范围(选择哪些部门可以看到)
  • 回调 URL(重定向地址)
  1. 获取应用的 AgentID 和 Secret

测试

使用 curl 测试 API:

# 获取用户信息
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

参考文档

常见问题

Q: 如何处理 Token 过期?
A: 实现 Token 刷新机制,当收到 40001 错误时重新获取 access_token。

Q: 如何限制访问范围?
A: 在后端验证用户身份后,根据用户的部门和角色进行权限判断。

Q: 可以获取用户哪些信息?
A: 根据授权范围(scope)和应用权限,可获取用户ID、名称、手机、邮箱、部门等。