From cbd0a96ce7f8877229766f2f36de5bddb0de9bcb Mon Sep 17 00:00:00 2001 From: Blizzard Date: Tue, 21 Jul 2026 10:02:09 +0800 Subject: [PATCH] =?UTF-8?q?docs(deploy):=20=E5=BE=AE=E4=BF=A1=20token=20?= =?UTF-8?q?=E4=B8=AD=E6=8E=A7=E6=94=B9=E7=94=A8=20frp=20stcp=20=E9=9A=A7?= =?UTF-8?q?=E9=81=93=EF=BC=88=E4=B8=8D=E4=B8=8A=E5=85=AC=E7=BD=91=EF=BC=8C?= =?UTF-8?q?=E6=97=A0=E9=9C=80=E5=9F=9F=E5=90=8D=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户腾讯云未配域名、frp 是 toml。改成:腾讯云 token 服务只绑 127.0.0.1, 经 frp stcp(点对点加密隧道)让 132 拉取,token 全程不上公网、不用证书。 - 说明书给出 toml 版 frp 配置(腾讯云 [[proxies]] stcp + 132 [[visitors]])、 cron 换 token 脚本、切换验证步骤。 - compose:gateway 加 extra_hosts host.docker.internal:host-gateway —— 容器里的 127.0.0.1 是容器自己,token 落在宿主机 127.0.0.1:9099,须经 host.docker.internal 访问。 代码侧(PullToken + accessToken 中控分支)无改动,沿用上一提交。 Co-Authored-By: Claude Opus 4.8 --- deploy/132-app/docker-compose.yml | 4 ++ deploy/wechat-token-zhongkong.md | 107 +++++++++++++++++------------- 2 files changed, 64 insertions(+), 47 deletions(-) diff --git a/deploy/132-app/docker-compose.yml b/deploy/132-app/docker-compose.yml index 4b884f4..055cfa4 100644 --- a/deploy/132-app/docker-compose.yml +++ b/deploy/132-app/docker-compose.yml @@ -39,6 +39,10 @@ services: WECHAT_TOKEN_URL: ${WECHAT_TOKEN_URL:-} WECHAT_TOKEN_SECRET: ${WECHAT_TOKEN_SECRET:-} ports: ["3000:8080"] # frp 外网 → 132:3000 → 容器 8080 + # 让容器能访问宿主机端口(微信 token 中控经 frp stcp 落在宿主机 127.0.0.1:9099, + # 容器里的 127.0.0.1 是容器自己,须用 host.docker.internal 指到宿主机)。 + extra_hosts: + - "host.docker.internal:host-gateway" volumes: # 微信支付证书(商户私钥 + 微信支付公钥):宿主机 132 的目录只读挂进容器。 # ⚠️ admin「系统配置 → 支付」里填的路径必须是**容器内路径**(/etc/sundynix/wechat-cert/...), diff --git a/deploy/wechat-token-zhongkong.md b/deploy/wechat-token-zhongkong.md index 8ffcbc7..3d5227c 100644 --- a/deploy/wechat-token-zhongkong.md +++ b/deploy/wechat-token-zhongkong.md @@ -1,84 +1,97 @@ -# 微信 access_token 中控(腾讯云静态 IP) +# 微信 access_token 中控(腾讯云静态 IP 换 token,经 frp 隧道拉取) ## 为什么 -gateway 换 access_token 时微信看到的是**本地宽带出网 IP(动态,会变)**,一变白名单 -就失效、二维码建不出来(40164)。微信官方推荐「中控服务器」:一台**固定 IP** 的机器 -统一换 token,业务机拉取使用。腾讯云那台(`162.14.122.200`,静态)正好当中控。 +gateway 换 access_token 时微信看到的是**本地宽带出网 IP(106.58.232.43,动态会变)**, +一变白名单就失效、二维码建不出来(40164)。微信官方推荐「中控服务器」:一台**固定 IP** +的机器统一换 token,业务机拉取使用。腾讯云那台(`162.14.122.200`,静态)当中控。 微信 IP 白名单**只限制换 token 这一步**(用 appsecret),拿 token 建二维码不查 IP。 -所以:腾讯云换 token → gateway 拉 token → gateway 本地建二维码。白名单只填腾讯云 IP。 +所以:腾讯云换 token → gateway 经 frp 隧道拉 token → gateway 本地建二维码。 +白名单只填腾讯云 IP,永不失效。 -## 腾讯云上做(三步) +token 全程**不上公网**:腾讯云 token 服务只绑 127.0.0.1,靠 frp stcp(点对点加密隧道) +让 132 访问。frp 拓扑:腾讯云=frps、132=frpc,均 **toml** 配置。 + +## 一、腾讯云侧(frps 那台) ### 1. 换 token 脚本 `/home/workspace/wxtoken/refresh.sh` - ```bash #!/bin/bash -# 用 appid/secret 换 access_token,写成 token.json。从腾讯云静态 IP 发出。 APPID="wxc236cddde8e7f863" -SECRET="你的公众号 AppSecret" # 建议重置后填新的 +SECRET="你的公众号 AppSecret" # 建议重置后填新的 OUT="/home/workspace/wxtoken/token.json" - resp=$(curl -s "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${APPID}&secret=${SECRET}") # 只在拿到 access_token 时才覆盖,避免偶发失败把好文件冲掉 echo "$resp" | grep -q '"access_token"' && echo "$resp" > "$OUT.tmp" && mv "$OUT.tmp" "$OUT" ``` - ```bash chmod +x /home/workspace/wxtoken/refresh.sh -/home/workspace/wxtoken/refresh.sh # 先手动跑一次,确认 token.json 生成 +/home/workspace/wxtoken/refresh.sh # 手动跑一次,确认 token.json 生成 +cat /home/workspace/wxtoken/token.json # 应有 {"access_token":"...","expires_in":7200} ``` -### 2. cron 每 ~1.5 小时刷新(token 有效 2h,留足余量) - +### 2. cron 每 90 分钟刷新(token 有效 2h,留余量) ```bash crontab -e -# 加一行: -*/90 * * * * /home/workspace/wxtoken/refresh.sh +# 加:*/90 * * * * /home/workspace/wxtoken/refresh.sh ``` -### 3. nginx 把 token.json 以 HTTPS + 密钥吐出 - -在腾讯云那台的站点配置里加(token 是 2h 高权限凭证,必须 HTTPS + 密钥,不能裸奔): - -```nginx -location = /wxtoken/token.json { - # 共享密钥校验:gateway 拉取时带 Authorization: Bearer <同一串> - if ($http_authorization != "Bearer 换成你自定的强随机串") { return 403; } - default_type application/json; - alias /home/workspace/wxtoken/token.json; - add_header Cache-Control "no-store"; -} -``` - -reload nginx 后自测(换成真域名): +### 3. 本地 HTTP 服务只绑 127.0.0.1(不出公网,交给 frp) ```bash -curl -s -H "Authorization: Bearer 换成你自定的强随机串" https://<腾讯云域名>/wxtoken/token.json -# 应返回 {"access_token":"...","expires_in":7200} -curl -s https://<腾讯云域名>/wxtoken/token.json # 不带密钥应 403 +cd /home/workspace/wxtoken && nohup python3 -m http.server 9099 --bind 127.0.0.1 >/dev/null 2>&1 & +# 建议做成 systemd 服务常驻,重启不丢 ``` -## gateway 侧(在 132 的 .env 里配,随部署生效) - -``` -WECHAT_TOKEN_URL=https://<腾讯云域名>/wxtoken/token.json -WECHAT_TOKEN_SECRET=换成你自定的强随机串 # 与 nginx 那串一字不差 +### 4. 腾讯云 `frpc.toml` 追加(暴露本地 9099 给隧道) +> 腾讯云跑着 frps;要把它自己的本地服务经 stcp 暴露,需在该机也跑一个 frpc(或复用已有)。 +```toml +[[proxies]] +name = "wxtoken" +type = "stcp" +secretKey = "换成一串强随机密钥" # 与 132 visitor 一致 +localIP = "127.0.0.1" +localPort = 9099 ``` -配了 gateway 就**只从中控拉、不自己换 token**;不配则维持直连(受本地 IP 白名单约束)。 +## 二、132 侧(frpc 那台) -## 切换 + 验证(关键,坐实 qrcode 不受 IP 限制) +`frpc.toml` 追加一个 visitor,把腾讯云的 token 服务映射到 132 本地 9099: +```toml +[[visitors]] +name = "wxtoken-visitor" +type = "stcp" +serverName = "wxtoken" +secretKey = "换成同一串强随机密钥" # 与腾讯云那串一字不差 +bindAddr = "127.0.0.1" +bindPort = 9099 +``` +reload frpc 后自测(在 132 上): +```bash +curl -s http://127.0.0.1:9099/token.json # 应返回腾讯云那份 token +``` -1. 先让**本地 IP 和腾讯云 IP 都在白名单**,配好上面 env、部署,扫码确认能登录(此时不确定 - token 是中控给的还是本地换的都能过)。 +## 三、gateway(132 的 .env) +``` +# 容器里的 127.0.0.1 是容器自己,须用 host.docker.internal 指到宿主机 +WECHAT_TOKEN_URL=http://host.docker.internal:9099/token.json +WECHAT_TOKEN_SECRET= +``` +- compose 已加 `extra_hosts: host.docker.internal:host-gateway`(容器可达宿主机端口)。 +- 密钥留空即可:stcp 的 secretKey 已是网络层鉴权,token 不上公网。 +- 配了 gateway 就**只从中控拉、不自己换 token**;不配则维持直连。 + +## 四、切换 + 验证(关键,坐实 qrcode 不受 IP 限制) + +1. 先让**本地 IP 和腾讯云 IP 都在白名单**,配好上面一切、部署,扫码确认能登录。 2. 去公众平台把**本地 IP 从白名单删掉,只留腾讯云 `162.14.122.200`**,再扫码: - - **能登录** → 换 token 走了中控、建二维码本地也没被拦,方案坐实,IP 隐患彻底消除。 + - **能登录** → 换 token 走了中控、建二维码本地也没被拦,方案坐实、IP 隐患彻底消除。 - **建二维码报 40164** → qrcode 也受 IP 限制,改用备选:腾讯云装 tinyproxy 正向代理, - gateway 加 `WECHAT_HTTP_PROXY` env(代码侧已预留思路,需再改一处 http 客户端)。 + gateway 加 `WECHAT_HTTP_PROXY`(需再改一处 http 客户端)。 ## 排查 - gateway 日志 `[wxlogin] 建二维码失败: ... 40164` → 中控没生效或没配 env,仍在本地换 token -- 中控 token.json 过期/为空 → 看 cron 有没有在跑、`refresh.sh` 手动能否生成 -- gateway 拉取 403 → `WECHAT_TOKEN_SECRET` 与 nginx 里的密钥不一致 +- 132 上 `curl 127.0.0.1:9099/token.json` 不通 → frp stcp 没打通(检查两端 secretKey 一致、frpc 都 reload 了) +- token.json 过期/为空 → cron 没跑或 refresh.sh 手动生成失败 +- 容器内拉不到(宿主机能拉到)→ compose 的 extra_hosts 没生效,或用了 127.0.0.1 而非 host.docker.internal