跳转至

企业微信集成

本文介绍两种企业微信集成方式:使用 Nginx 与 Flask 为自建服务接入企业微信扫码认证,以及部署企业微信 AI 机器人。

🟢自建服务接入扫码认证

准备工作

  • 服务器:一台能运行 Nginx 和 Python 的服务器。
  • 域名:已备案并解析到服务器 IP(例如 login.tech.intra.qq.com)。
  • 企业微信:已开通的企业微信账号,并创建应用,获取 AgentIDCorpIDSecret

Nginx 配置

Nginx 作为反向代理,拦截未认证请求并重定向到企业微信扫码登录页面。

upstream auth_backend {
    server 10.44.3.7:39891; # Flask 应用的 IP 地址和端口
}

server {
    listen 80;
    server_name help_qice.tech.intra.qq.com; # 你的域名
    expires 1m;

    # 鉴权验证接口(内部调用)
    location /auth/ {
        proxy_pass http://auth_backend/auth/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_redirect off;
    }

    # 需要鉴权的路径(例如 /chat/ 及其子路径)
    location ~ ^/chat/[^/]+$ {
        auth_request /auth/verify;   # 访问 /auth/verify 接口进行鉴权
        error_page 401 = @login;     # 鉴权失败,重定向到 @login
        proxy_pass http://10.66.244.44:9098; # 代理到后端主路径
    }

    # 登录重定向
    location @login {
        return 302 /auth/login?next=$request_uri; # 传递原始请求 URI
    }

    # admin
    location /admin/ {
        return 302 https://wework.qpic.cn/wwpic3az/653855_6HL_o5VRSWmKTJY_1754391309;
        proxy_pass http://10.66.244.44:9098/admin;
    }

    # 其他静态资源或无需鉴权的路径
    location / {
        add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0";
        add_header Pragma "no-cache";
        add_header Expires "0";
        proxy_pass http://10.66.244.44:9098/;
    }
}
配置项 说明
server_name 改为你的域名
auth_backend 改为运行 Flask 应用的服务器 IP 和端口
location ~ ^/chat/[^/]+$ 定义需要进行企业微信认证的路径,可按需修改
proxy_pass 你的应用后端地址
sudo nginx -t        # 检查配置是否正确
sudo nginx -s reload # 重新加载 Nginx

Flask 认证应用 auth.py

Flask 应用负责处理企业微信扫码登录流程、验证用户身份并设置 Session。

pip install Flask requests
auth.py
from flask import Flask, request, redirect, session, url_for, jsonify
import requests
import os

app = Flask(__name__)
app.secret_key = 'your_secure_secret_key_here'  # 请替换为安全密钥

# 企微扫码登录配置
WEIXIN_AGENTID = 123456                                       # 你的 AgentID
WEIXIN_CORPID = "wx123456"                                    # 你的 CorpID
WEIXIN_SECRET = 'AAAAAAAAAAAAAAAAAAAAAAAAAAA'                 # 你的 Secret
REDIRECT_URI = 'http://login.tech.intra.qq.com/help_qice/auth/callback'  # 回调域名
STATE = 'auth_state'                                          # 防 CSRF,建议每次动态生成

# 获取 access_token(用于后续 API 调用)
def get_access_token():
    url = f"https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid={WEIXIN_CORPID}&corpsecret={WEIXIN_SECRET}"
    resp = requests.get(url)
    data = resp.json()
    if data['errcode'] == 0:
        return data['access_token']
    else:
        return None

# 主页或测试
@app.route('/')
def index():
    return '''
    <h2>认证系统</h2>
    <p><a href="/chat">访问 /chat(触发鉴权)</a></p>
    '''

# 扫码登录入口
@app.route('/auth/login')
def login():
    redirect_url = f"https://login.work.weixin.qq.com/wwlogin/sso/login?login_type=CorpApp&appid={WEIXIN_CORPID}&agentid={WEIXIN_AGENTID}&redirect_uri={REDIRECT_URI}&state=STATE"
    next_url = request.args.get('next', '/chat')  # 默认 fallback
    session['next'] = next_url                    # 保存跳转目标
    return redirect(redirect_url)

# 扫码回调
@app.route('/auth/callback')
def callback():
    code = request.args.get('code')
    state = request.args.get('state')

    if not code:
        return "登录失败:缺少 code", 400

    access_token = get_access_token()
    if not access_token:
        return "无法获取 access_token", 500

    # 使用 code 换取用户信息
    user_info_url = f"https://qyapi.weixin.qq.com/cgi-bin/user/getuserinfo?access_token={access_token}&code={code}"
    resp = requests.get(user_info_url)
    data = resp.json()

    if data.get('errcode') != 0:
        return f"获取用户信息失败:{data.get('errmsg')}", 403

    user_id = data.get('UserId')  # 内部成员
    # open_userid = data.get('OpenUserId')  # 外部用户(如有)

    if not user_id:
        return "未获取到用户身份", 403

    # 认证成功,写入 session
    session['user_id'] = user_id  # (2)!

    next_url = session.pop('next', '/chat')  # 取出并删除
    return redirect(next_url)

# Nginx auth_request 调用的验证接口
@app.route('/auth/verify')
def verify():
    if 'user_id' in session:
        return '', 200              # 成功,允许访问
    else:
        return 'Unauthorized', 401  # 拒绝,触发登录

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=39891, debug=True)
配置 / 路由 说明
app.secret_key 安全的随机字符串,用于 Session 加密
WEIXIN_AGENTID / WEIXIN_CORPID / WEIXIN_SECRET 替换为企业微信后台获取的参数
REDIRECT_URI 必须与企业微信后台配置的回调域名一致
/auth/login 构造扫码登录 URL 并重定向
/auth/callback 处理扫码回调,获取用户信息并设置 Session
/auth/verify Nginx 用于验证用户是否已登录的接口
python auth.py

上线前必改

app.secret_keyWEIXIN_*REDIRECT_URI 都是示例值;STATE 注释已注明「建议每次动态生成」,代码里目前是固定字面量,正式使用需替换。

测试

  • 在浏览器访问需认证的路径,如 http://help_qice.tech.intra.qq.com/chat/demo
  • 未登录时会被重定向到企业微信扫码登录页面。
  • 使用企业微信 App 扫码登录。
  • 认证成功后重定向回之前访问的页面。

多服务复用同一回调地址

企业微信的回调地址一般只能配置一个,可将其配置为 http://login.tech.intra.qq.com,再通过以下 Nginx 配置让多个独立服务复用同一回调地址。

server {
    listen 80;
    server_name login.tech.intra.qq.com;   # 优先处理的重定向
    location / {
        # http://login.tech.intra.qq.com/pbc/view_svn_log/4
        #   ==> http://pbc.tech.intra.qq.com/view_svn_log/4
        rewrite ^/([^/]+)/(.+)$ http://$1.tech.intra.qq.com/$2 last;
    }
}

rewrite 逻辑解析:正则 ^/([^/]+)/(.+)$([^/]+) 捕获第一个路径段落(如 pbc,用 $1 引用),(.+) 捕获剩余路径(用 $2 引用,包含查询参数),据此把“登录域 + 子路径”转换为“子域 + 剩余路径”。

原始请求 捕获结果
http://login.tech.intra.qq.com/pbc/view_svn_log/4 $1=pbc$2=view_svn_log/4http://pbc.tech.intra.qq.com/view_svn_log/4
http://login.tech.intra.qq.com/api/v1/data?param=1 $1=api$2=v1/data?param=1

标志位 last 表示在当前 server 上下文中停止进一步重写,用新 URL 重新开始请求处理(Nginx 内部处理,不向客户端发送重定向)。


🟢部署企业微信 AI 机器人

选用 Google Gemini 主要是反应快、几乎不限流、免费,适合放在企微上给多人使用。

获取 API Key:makersuite.google.com/app/apikey

docker-compose.yml

docker-compose.yml
version: '3.4'
services:

  chatgpt:
    image: admibo/chatgpt-robot
    restart: always
    ports:
      - 5001:5001
    environment:
      LANG: 'C.UTF-8'
    networks:
      - ai-robot
    volumes:
      - ./config.cfg:/app/config.cfg
      - ./data:/app/data
      - ./presets:/app/presets
    depends_on:
      - gemini-openai-proxy

  gemini-openai-proxy:
    image: admibo/gemini-openai-proxy
    restart: always
    networks:
      - ai-robot

networks:
  ai-robot:

config.cfg

config.cfg
[openai]
api_endpoint="http://gemini-openai-proxy:8000/v1" # 注意这里的端点不要动

[[openai.accounts]]
api_key = "google-gemini key(使用默认配置的 endpoint)或者 sk-xxx(gpt 需要使用官方或其他可用的 endpoint)"
mode = "browserless"
paid = false

[wecom]
# 企业微信相关设置
# 企业微信管理后台:https://work.weixin.qq.com/wework_admin/frame
# 回调地址需要能够被公网访问
host = "0.0.0.0"
port = 5001
debug = false
# 企业 ID:我的企业 -> 企业信息 -> 企业 ID
corp_id = ""
# 应用 AgentId:应用管理 -> 自建 -> 创建应用 -> AgentId
agent_id = 1000188
# 应用 Secret:应用管理 -> 自建 -> 创建应用 -> Secret
secret = ""
# API 回调地址请填写 http://公网ip:5001/wechat
# API 令牌:应用管理 -> 自建 -> 创建应用 -> 功能 -> 接收消息 -> 启用API接收 -> 随机生成的 Token
token = "qGFu1"
# API 加解密密钥:同上,随机生成的 EncodingAESKey
encoding_aes_key = "zQi5oft11dwzIYnAf4qedLPnE3zh"

启动

docker-compose up -d

相关文件放置位置

github.com/kaozb/tools/tree/main/robot

参考项目