logo
0
0
WeChat Login

wechat_forward — 微信公众号消息转发器

一个用 Go 编写的微信公众号消息转发器:解决「公众号后台只能配置一个回调地址,但实际有多个业务/平台需要消费这些事件」的问题。

特性

  • 支持配置多个公众号(每个公众号独立 AppID/Token/EncodingAESKey)
  • 一个公众号可以配置多条转发路由,按消息类型/事件/关键字等规则进行路由
  • 支持「主路由(IsPrimary)」:其响应作为公众号回包,其余路由异步并发转发
  • 支持公众号 3 种安全模式:明文 / 兼容 / 安全(AES)
  • 自带轻量管理 Web UI(账号、路由、转发日志的 CRUD)
  • 配置使用 YAML,数据使用 SQLite(单文件,零外部依赖)
  • 单文件可执行,前端资源 embed 内嵌,部署只需一个二进制 + 一个 YAML

项目结构

wechat_forward/ ├── cmd/server/ # 主程序入口 ├── internal/ │ ├── config/ # 配置加载 │ ├── storage/ # SQLite 数据层(GORM) │ ├── wechat/ # 微信签名校验、AES 加解密、消息解析 │ ├── router/ # 路由匹配引擎 │ ├── forwarder/ # HTTP 转发 + 日志 │ ├── handler/ # 微信回调 / 管理 API │ └── server/ # HTTP 服务装配 ├── web/ # 管理面板(HTML/CSS/JS) ├── config.yaml.example ├── go.mod / go.sum └── README.md

快速开始

# 1. 安装依赖(首次) go mod tidy # 2. 复制配置 cp config.yaml.example config.yaml # 3. 启动 go run ./cmd/server -config config.yaml # 或先编译 go build -o wechat_forward ./cmd/server ./wechat_forward -config config.yaml

启动后:

  • 微信回调地址:http(s)://你的域名/wechat/{AppID}
  • 管理面板:http://localhost:8080/admin(默认账号 admin / admin
  • 健康检查:http://localhost:8080/healthz

公众号后台配置

进入公众号后台 → 开发 → 基本配置 → 服务器配置:

字段填写
URLhttp(s)://你的域名/wechat/{AppID}
Token与平台中该公众号配置的 token 一致
EncodingAESKey加密模式(兼容/安全)下需填写 43 位
消息加解密方式与平台中该公众号 encrypt_mode 一致

转发协议

本服务采用原样透传(reverse-proxy 风格):

  • HTTP method、URL query(signature/timestamp/nonce/encrypt_type/msg_signature/openid 等)全部保留并附加到 target_url 之后
  • 请求体(XML,明文或加密报文)原样转发,不做任何重新封装
  • 请求头原样透传(自动剔除 Host / Content-Length / 各类 hop-by-hop 头),所以后端可以直接使用任意一个开源「微信公众号 SDK」对接收到的请求做校验、解密
  • 路由可以在管理面板里配置「附加请求头(JSON)」,以追加自定义头(如 Authorization),覆盖原始头
  • 主路由(IsPrimary)的响应(status / headers / body)会原样回写给微信,等同于该后端就是微信回调地址

路由匹配的解析

为了路由匹配(按 msg_type / event / keyword 分流),本服务会本地解析消息:

  • 明文模式:直接 XML 解析
  • 兼容/安全模式:尝试用 EncodingAESKey 解密后再 XML 解析

无论哪种模式,转发出去的 body 仍是微信发来的原始 body,未做任何修改。后端拿到的就是 100% 一致的微信原始请求。

路由匹配(match_type)

类型含义match_value 示例
all匹配所有消息(留空)
msgtype按消息类型,多个用逗号text,image,event
event按事件类型(仅 MsgType=eventsubscribe,unsubscribe,CLICK
keyword文本消息内容包含;re: 前缀启用正则订单re:^退款.*

主路由(IsPrimary)

微信公众号要求 5 秒内返回回包,因此本服务对回包采用以下策略:

  • 在所有命中的路由中最多选择 1 条主路由(多条主路由按 priorityid 排序取第一条)
  • 主路由被同步等待,其响应(HTTP status / headers / body)原样回写给微信
    • 后端可以返回 success、空响应、或完整的微信回包 XML(明文 / encrypt_type=aes 时为加密 XML)
    • 后端就是「直连微信」的写法,本服务不做任何改写
  • 其余路由会异步并发转发,其响应不会用于公众号回包,但会进入转发日志

启动配置(config.yaml)

server: listen: ":8080" storage: driver: sqlite dsn: data/wechat_forward.db admin: enabled: true path: /admin username: admin password: admin forward: concurrency: 16 # 单次扇出最大并发 default_timeout_ms: 3000 # 路由默认超时 max_body_kb: 512 # 响应体保存上限(KB) log_retention_rows: 5000 # 预留:日志保留条数 log: level: info # debug | info | warn | error

API 速览(管理)

所有管理 API 都在 /admin/api/ 下,需要 Basic Auth。

  • 公众号
    • GET /admin/api/accounts
    • POST /admin/api/accounts
    • GET /admin/api/accounts/{id}
    • PUT /admin/api/accounts/{id}
    • DELETE /admin/api/accounts/{id}
  • 路由
    • GET /admin/api/routes?account_id=1
    • POST /admin/api/routes
    • GET /admin/api/routes/{id}
    • PUT /admin/api/routes/{id}
    • DELETE /admin/api/routes/{id}
  • 日志
    • GET /admin/api/logs?account_id=1&limit=100&offset=0
    • DELETE /admin/api/logs?account_id=1

安全建议

  • 部署在 HTTPS 反向代理后(Nginx / Caddy)
  • 修改默认 admin.username / admin.password
  • 需要更高安全性时启用「兼容」或「安全」加密模式
  • 把管理面板挂到内网或加 IP 白名单(可在反代里限制 /admin/

开发

go vet ./... go build ./...