畅播科技CHANGBO TECH ← 返回官网 Demo
DEVELOPMENT & DEPLOYMENT PLAN

H5 官网 + 微信登录
开发与部署方案

面向「微信服务号网页授权登录」的公司官网 Demo。本文给出可直接落地的技术架构、OAuth 时序、公众号后台配置清单、开发阶段划分与三套部署路径,并明确当前 Demo 与真实上线之间的差距。

服务号原始ID:gh_2fba6debb5b2 AppID:wx6ff6319c3ed127f6 scope:snsapi_userinfo 版本:v1.0 · Demo
01

先看这四条关键结论

这些约束决定了方案形态,建议在动工前先与公众号管理员逐条确认。

!
服务号网页授权只在「微信内置浏览器」生效

公司官网常被 PC / 外部浏览器访问,此时 wx6ff6319c3ed127f6 的网页授权无法唤起。PC 端需另在微信开放平台申请「网站应用」,用微信扫码登录(另一套 AppID)。两端通过 UnionID 统一身份 —— 前提是把该服务号绑定到同一个开放平台账号。本 Demo 对非微信浏览器做了检测与降级提示。

!
必须是「已微信认证的服务号」

只有认证的企业主体服务号具备「网页授权获取用户基本信息」(snsapi_userinfo) 权限;订阅号(无论是否认证)不支持。权限不足时接口返回 48001 / 10005。请先在公众号后台「设置与开发 → 接口权限」确认该项状态。

i
原始 ID 与 AppID 的分工

gh_2fba6debb5b2 是服务号原始 ID,仅用于搜索/关注/后台识别,不参与 OAuth;授权链接用的是 AppID wx6ff6319c3ed127f6。此外还需 AppSecret(管理员在「基本配置」中查看/重置),本次未提供,且绝不能放进前端代码或 Git 仓库。

✓
Demo 已完成到哪里

本次交付:完整 H5 官网(响应式、登录态、会员中心、环境检测)、可演示的模拟授权流程、以及可直接运行的 OAuth 服务端参考实现。默认 WX_MODE=demo 不调用微信接口;配置好域名、AppSecret 与网页授权域名后切到 live 即为真实登录。

02

技术架构

前端只负责展示与跳转,所有涉及 AppSecret 的动作全部收敛在服务端(BFF)。

微信内置浏览器用户点击「微信登录」
→
H5 静态站点public/ · HTML/CSS/JS
→
Node BFFserver/server.js · 持有 AppSecret
→
微信接口open / api.weixin.qq.com
state 防 CSRFcode 一次性换取HttpOnly 会话 CookieOpenID / UnionID 落库前端只见脱敏资料

PC / 外部浏览器分支:微信开放平台「网站应用」扫码登录 → 同一 UnionID → 同一会员体系(需另行申请 AppID)。

层本次 Demo 选型生产建议理由
前端原生 HTML / CSS / JS,零构建保持零依赖,或换 Vue3 + ViteH5 官网首屏优先,无构建链最省运维;业务复杂再上框架
服务端Node.js 18+ 内置 http,零依赖Express / NestJS + PM2,或云函数 / 微信云托管参考实现零依赖便于阅读与部署;生产需日志、限流、结构化错误处理
会话内存 Map + sid CookieRedis / 数据库会话表 + 签名 Cookie内存会话进程重启即失效,多实例无法共享
用户数据内存 MapMySQL / PostgreSQL / 云数据库,按 unionid 唯一索引持久化身份、支持跨端统一与后续会员运营
接入层Node 直出静态Nginx HTTPS 反代 + CDN/COS 托管静态资源静态资源走 CDN,Node 只处理 /wx /api
03

项目目录与运行方式

# 项目根目录 h5_login/
├── public/                  # H5 静态站点(可单独静态托管)
│   ├── index.html           # 官网首页 + 登录弹窗 / 会员中心
│   ├── plan.html            # 本方案页
│   ├── assets/
│   │   ├── styles.css       # 官网样式(移动端优先)
│   │   ├── plan.css         # 方案页样式
│   │   └── app.js           # 登录态、环境检测、演示授权流程
│   └── MP_verify_XXXX.txt   # 公众号域名校验文件(上线时放入)
├── server/
│   └── server.js            # OAuth 回调 + 会话 + 静态服务(零依赖)
├── deploy/
│   └── nginx.conf.example   # HTTPS / 反代 / 校验文件配置
├── Dockerfile               # 容器化部署
├── package.json             # npm start → node server/server.js
├── .env.example             # 配置模板(复制为 .env)
└── .gitignore               # 忽略 .env

# 本地运行
cp .env.example .env
npm start                    # http://localhost:3000

前端 app.js 中的 CONFIG.apiBase 默认为同源;若前后端分离部署,改成后端域名并同步处理 CORS 与 credentials: 'include'。生产环境建议由 /api/config 统一下发模式与公开配置,而不是硬编码在前端。

04

网页授权登录时序(OAuth 2.0)

snsapi_userinfo 会弹出授权页并返回昵称/头像;若只要 OpenID,可改用静默的 snsapi_base。

1
前端发起:跳服务端而不是直接拼微信链接

点击登录 → GET /wx/authorize?redirect=/。服务端生成随机 state(5 分钟有效、一次性),写入 wx_state Cookie,并记录登录后的回跳地址。redirect 必须校验为站内相对路径,防止开放重定向。

2
302 到微信授权页

由服务端拼接(AppSecret 不出现在这里):

https://open.weixin.qq.com/connect/oauth2/authorize
  ?appid=wx6ff6319c3ed127f6
  &redirect_uri=https%3A%2F%2Fwww.yourcompany.com%2Fwx%2Fcallback
  &response_type=code
  &scope=snsapi_userinfo
  &state=STATE#wechat_redirect

redirect_uri 的域名必须与公众号后台「网页授权域名」完全一致(含 www 与否)。

3
微信回调,校验 state

用户同意后微信重定向到 /wx/callback?code=CODE&state=STATE。服务端比对 Cookie 中的 state、检查是否过期并立即删除;code 一次性、有效期 5 分钟。用户取消则走友好提示页。

4
code 换 openid(服务端)
GET https://api.weixin.qq.com/sns/oauth2/access_token
  ?appid=wx6ff6319c3ed127f6
  &secret=APPSECRET          // 仅服务端可见
  &code=CODE
  &grant_type=authorization_code
→ { access_token, openid, unionid?, scope, expires_in }
5
拉取用户资料(服务端)
GET https://api.weixin.qq.com/sns/userinfo
  ?access_token=...&openid=...&lang=zh_CN
→ { nickname, headimgurl, sex, city, province, country, unionid }

按 unionid(无则 openid)upsert 用户表。不要把 access_token、完整 openid 返回前端。

6
建立自有会话并回到页面

生成 sid,写 HttpOnly; Secure; SameSite=Lax Cookie,清掉 wx_state,302 回 /?login=success。前端调用 /api/user/me 拿脱敏资料并渲染登录态;会员中心等受保护内容由服务端会话鉴权控制。

05

服务端接口清单

路径方法作用鉴权 / 备注
/api/configGET下发公开配置(mode、AppID、scope)无;绝不含 AppSecret
/wx/authorizeGET生成 state、写 Cookie、302 到微信redirect 站内白名单校验;demo 模式返回 503
/wx/callbackGET接收 code,换取身份并建立会话state + Cookie 双重校验、code 一次性
/api/user/meGET返回当前登录用户(脱敏)sid Cookie;未登录返回 loggedIn:false
/api/user/logoutPOST销毁会话、清 Cookiesid Cookie
/healthzGET健康检查 / 探活无;供负载均衡与监控使用
/*GET静态站点与 MP_verify_* 校验文件路径穿越防护 + MIME 白名单
06

微信公众平台后台配置清单

路径基于当前公众平台界面;不同账号类型菜单名称可能略有差异,以实际后台为准。

#位置要做什么不做的后果
1设置与开发 → 接口权限确认「网页服务 → 网页授权获取用户基本信息」为已获得;账号为已认证服务号48001 api unauthorized / 10005 无 scope 权限
2设置与开发 → 基本配置取得 AppSecret(管理员扫码,重置会使旧密钥失效),写入服务器 .env;把服务器出口公网 IP 加入 IP 白名单调用 /cgi-bin/token 类接口报 40164 invalid ip
3公众号设置 → 功能设置 → 网页授权域名填 www.yourcompany.com(不带协议、路径、端口;最多 20 个)。下载 MP_verify_XXXX.txt 放到 public/ 根目录,确保 https://域名/MP_verify_XXXX.txt 可访问后再提交授权页提示「redirect_uri 参数错误」,微信内无法打开
4公众号设置 → 功能设置 → JS 接口安全域名如需微信分享 / 扫一扫等 JS-SDK 能力再配置(最多 5 个)JS-SDK 签名校验失败
5微信开放平台 open.weixin.qq.com绑定该服务号以获取 UnionID;PC 扫码登录另建「网站应用」并通过审核官网 PC 端无法微信登录;多端身份无法统一
6站点合规上线前补齐《用户协议》《隐私政策》、ICP 备案与主体信息,头像/昵称按最小必要收集审核与合规风险,微信内可能被拦截
i
本地开发不能填 localhost

网页授权域名只接受可公网访问的域名,且不识别端口/IP。开发阶段请用固定域名的 HTTPS 内网穿透(ngrok / cpolar / natapp)或先部署到测试服务器;也可用微信公众平台接口测试号(mp.weixin.qq.com/debug/cgi-bin/sandbox)拿独立测试 AppID/AppSecret 联调,避免改动正式号配置。测试号的网页授权域名配置以后台页面要求为准。

07

开发阶段与交付物

PHASE 1前置确认
  • 服务号认证状态 / 权限核验
  • AppSecret 与域名归属
  • 品牌名称、LOGO、真实文案
  • 确定是否覆盖 PC 扫码登录
  • 确定会话与用户存储方案
PHASE 2 · 已完成官网 Demo 前端
  • 响应式 H5 官网 6 个区块
  • 登录态 / 会员中心 / 退出
  • 微信内外环境检测与降级
  • 模拟授权四步流程
  • 接入状态调试面板
PHASE 3 · 已完成OAuth 服务端参考实现
  • 授权入口 + state 防 CSRF
  • code 换取 / 用户资料
  • 会话 Cookie 与接口
  • 静态托管 + 域名校验文件位
  • Nginx / Dockerfile 模板
PHASE 4联调与生产化
  • 测试号 / 正式号授权联调
  • 用户与会话落库
  • 限流、日志脱敏、监控告警
  • 隐私协议与备案
  • PC 扫码登录(如需)

建议顺序:先把 PHASE 2/3 用测试号 + 内网穿透跑通一次真实授权,再切换到正式 AppID 与生产域名,避免反复改动正式服务号配置。

08

部署方案(三选一或分阶段递进)

方案适用做法真实微信登录成本 / 门槛
A 演示预览内部评审、给客户看效果本地 npm start;或仅把 public/ 作为纯静态页托管(COS+CDN / Vercel / Netlify / 发布为应用)。手机预览配 HTTPS 内网穿透否,走模拟流程最低,无需域名与备案
B 联调测试开发期真实授权验证(推荐先做)测试号 AppID/AppSecret + 固定域名 HTTPS 隧道 + 本地/测试服务器 Node;WX_MODE=live是(测试号)低,不污染正式号配置
C 正式上线对外运营已备案域名 + SSL + 云服务器/容器 + Nginx 反代 + 数据库;或静态 CDN + Serverless 云函数 / 微信云托管承载 OAuth 回调是(正式号)需备案、认证服务号、证书与运维

方案 C · 标准云服务器上线步骤

  1. 域名 ICP 备案 → DNS 解析到服务器 → 申请并部署 SSL 证书(腾讯云/阿里云免费证书或 Let's Encrypt)。
  2. 部署代码:git clone → cp .env.example .env 填 WX_APPSECRET、PUBLIC_BASE_URL=https://www.yourcompany.com、SESSION_SECRET、WX_MODE=live → npm start 由 PM2 守护,或用 Dockerfile 构建镜像交给容器服务。
  3. 按 deploy/nginx.conf.example 配置 HTTPS、/wx /api 反代到 3000 端口、静态资源直出,并确保 MP_verify_XXXX.txt 可从域名根目录访问。
  4. 公众号后台配置「网页授权域名」与 IP 白名单(见第 06 节),保存后等待生效。
  5. 微信内打开 https://www.yourcompany.com 实测:授权弹窗 → 同意 → 回跳 → 头像昵称 → 刷新保持登录 → 退出 → 再次登录。
  6. 接入日志、错误告警、备份与 /healthz 探活;上线后按第 10 节清单验收。

方案 C 的两种替代运行形态

形态优点注意点
Serverless(云函数 + API 网关 / 静态 CDN)免运维、按量计费;静态站点与 OAuth 回调解耦云函数出口 IP 不固定,依赖 IP 白名单的接口需配 NAT 网关固定出口;会话必须存外部存储,不能放内存
微信云托管 / CloudBase与微信生态集成度高、免证书与服务器运维网页授权域名仍需自有已备案域名并绑定;默认分配域名不能替代域名归属校验
!
纯静态托管无法实现真实登录

只要没有服务端,AppSecret 就无处安放,也无法用 code 换取身份。方案 A 的静态链接只能演示交互;要真实登录,至少需要一个 HTTPS 接口承载 /wx/callback。

09

环境变量与安全基线

变量示例 / 来源说明
WX_APPIDwx6ff6319c3ed127f6可公开;用于拼授权链接
WX_APPSECRET公众号后台「基本配置」绝密;仅服务端环境变量 / 密钥管理,不进镜像、仓库、前端、日志
PUBLIC_BASE_URLhttps://www.yourcompany.com拼接 redirect_uri;须与网页授权域名一致
SESSION_SECRET32 位以上随机串生产用于会话签名 / 加密,定期轮换
WX_MODEdemo / live环境隔离,避免测试流量走正式号
PORT3000平台注入,服务监听 0.0.0.0
  • state:服务端随机生成、一次性、5 分钟过期,并与 Cookie 双重校验,防 CSRF 与重放。
  • redirect:只接受站内相对路径(拒绝 //、反斜杠、换行),防开放重定向。
  • Cookie:HttpOnly + Secure(HTTPS)+ SameSite=Lax;多个 Cookie 必须一次性以数组下发,避免被后一次 Set-Cookie 覆盖。
  • 数据最小化:不向前端返回完整 openid / unionid / access_token;头像昵称按需收集并在隐私政策中说明。
  • 运维:全站 HTTPS、日志脱敏、接口限流、依赖与证书定期更新、数据库备份。
10

上线验收清单与常见错误码

验收清单

  • 服务号为已认证服务号,「网页授权获取用户基本信息」权限已生效
  • 域名已备案、全站 HTTPS、证书有效,MP_verify_XXXX.txt 可从域名根目录访问
  • 公众号后台网页授权域名与 redirect_uri 域名完全一致(含 www)
  • 服务器出口公网 IP 已加入 IP 白名单;AppSecret 只存在于服务端环境变量
  • 微信内:点击登录 → 授权页 → 同意 → 回跳 → 显示昵称头像 → 刷新仍为登录态 → 退出后受保护内容不可见
  • 非微信浏览器:给出明确降级提示,或已接入开放平台网站应用扫码登录
  • state 重放、code 复用、取消授权、网络超时均有友好提示,无空白页
  • 《用户协议》《隐私政策》、ICP 备案号、真实公司信息与联系方式已替换 Demo 占位内容
  • 日志与监控可见授权成功率、错误码分布;/healthz 探活正常

常见错误码速查

错误含义处理
48001 / 10005接口未授权 / 无 scope 权限确认认证服务号身份与网页授权权限;检查认证是否生效
10003 / redirect_uri 参数错误回调域名与后台配置不一致核对网页授权域名、www、协议与 URL 编码
40029 / 40163code 无效 / 已被使用code 一次性且 5 分钟有效,重新发起授权;检查多实例重复消费
40164调用方 IP 不在白名单把服务器出口公网 IP 加入公众号后台 IP 白名单,等待生效
40001 / 42001access_token 无效或过期区分「网页授权 token」与「全局 token」;全局 token 单点缓存,避免多服务互相刷新
微信内打不开 / 被拦截域名未备案、证书异常或外链规范问题换已备案域名、修复 HTTPS,必要时在公众平台申诉
✓
最小上线闭环

备案域名 + 证书 → 部署 Node 服务与静态站 → 放置校验文件 → 后台配网页授权域名 / IP 白名单 → 填写 AppSecret 切 WX_MODE=live → 微信内实测 → 补齐合规文案。完成这七步,本 Demo 即可变成可用的官网登录。