公众号网页授权统一域名方案
微信公众号网页授权:使用统一授权域名解决多环境 redirect_uri 限制使用统一微信授权域名接入公众号网页授权
一、背景
在微信公众号网页授权场景中,获取 openid 通常需要经过微信 OAuth 流程:
- 用户在微信内打开业务页面;
- 业务系统生成微信授权地址;
- 微信授权完成后回调
redirect_uri; - 后端拿到
code; - 后端使用
code换取 openid; - 系统生成自己的登录态或 token;
- 最后跳回前端页面。
这个流程本身并不复杂,但在真实业务中经常会遇到一个问题:公众号后台可配置的网页授权域名数量有限,而我们的系统往往有生产、pre、测试、demo 等多个环境。
例如:
生产环境:
https://wx.xxx.cn
测试环境:
https://test-wx.xxx.cn
pre 环境:
https://pre-wx.xxx.cn
demo 环境:
https://demo.xxx.cn如果每个环境都想直接作为微信 OAuth 的 redirect_uri 域名,很快就会遇到公众号后台域名数量不够的问题。
因此,一个更合理的方案是:使用一个统一的微信授权域名,例如 wx-auth.xxx.cn,集中承接微信回调,再根据环境或业务标识转发到对应系统。
二、为什么需要统一授权域名
微信网页授权域名配置的本质是:微信要求 redirect_uri 所属域名必须在公众号后台配置过。
也就是说,如果公众号后台配置的是:
wx-auth.xxx.cn那么微信允许回调:
https://wx-auth.xxx.cn/oauth/callback
https://wx-auth.xxx.cn/test/oauth/callback
https://wx-auth.xxx.cn/demo/oauth/callback但不会自动允许:
https://test-wx.xxx.cn/oauth/callback
https://demo.xxx.cn/oauth/callback
https://pre-wx.xxx.cn/oauth/callback因为这些都是不同域名。
如果系统只有一个生产环境,直接配置生产域名即可。但一旦系统存在多个环境,直接把每个环境都配置到公众号后台,就会带来几个问题:
- 域名名额不够;
- 测试环境和生产公众号绑定过深;
- appid、appsecret 容易分散到多个系统;
- 授权逻辑散落在不同环境,后期维护困难;
- 回调、登录态、跳转逻辑容易不一致。
统一授权域名的价值,就是把微信授权入口收敛起来。
三、推荐架构
推荐使用一个独立域名作为统一微信授权入口:
wx-auth.xxx.cn所有环境都通过它发起和接收微信授权回调。
整体流程如下:
用户打开业务页面
↓
业务系统判断需要微信授权
↓
跳转到 wx-auth.xxx.cn/oauth/start
↓
wx-auth 生成微信授权 URL
↓
微信回调 wx-auth.xxx.cn/oauth/callback?code=xxx&state=xxx
↓
wx-auth 使用 code 换 openid
↓
根据 state 判断来源环境
↓
生成内部临时票据或登录态
↓
302 跳回对应业务环境例如:
生产:
https://wx-auth.xxx.cn/prod/oauth/callback
测试:
https://wx-auth.xxx.cn/test/oauth/callback
demo:
https://wx-auth.xxx.cn/demo/oauth/callback虽然路径不同,但域名始终是:
wx-auth.xxx.cn这就只占用公众号后台的一个网页授权域名名额。
四、state 参数的作用
在统一授权域名方案中,state 非常关键。
因为微信回调时,只会把 code 和 state 带回来:
https://wx-auth.xxx.cn/oauth/callback?code=xxx&state=xxx所以我们需要通过 state 知道:
- 用户来自哪个环境;
- 用户授权前想访问哪个页面;
- 授权完成后应该跳回哪里;
- 是否存在 CSRF 风险;
- 这次授权请求是否过期。
一个比较实用的 state 内容可以包含:
{
"env": "demo",
"redirectUrl": "https://demo.xxx.cn/order/list",
"nonce": "随机字符串",
"timestamp": 1718000000000
}实际传给微信时,不建议直接明文传 JSON。可以使用以下方式之一:
方式一:state 只放 UUID,详细内容存在 Redis
方式二:state 使用加密后的字符串
方式三:state 使用签名后的短 token更推荐第一种:
state = uuid
Redis:
wx:oauth:state:{uuid} -> env、redirectUrl、nonce、timestamp这样 URL 更短,也更安全。
五、统一授权域名的两种实现方式
方案一:独立 wx-auth 服务
这是最标准的做法。
单独部署一个 wx-auth 服务,专门处理公众号授权相关逻辑:
wx-auth.xxx.cn
/oauth/start
/oauth/callback它负责:
- 生成微信 OAuth URL;
- 接收微信回调;
- 使用 code 换 openid;
- 维护 openid 与业务用户的映射;
- 根据 state 跳回不同环境;
- 统一处理异常和日志。
优点是架构清晰、职责单一、安全性高。
适合中长期方案。
方案二:Nginx 路径中转
如果当前没有时间单独开发 wx-auth 服务,也可以先使用 Nginx 做路径中转。
例如公众号后台已经配置了:
wx.xxx.cn但测试环境实际服务在:
https://demo.xxx.cn此时可以在 wx.xxx.cn 上配置一个路径:
http://wx.xxx.cn/testwx/然后由 Nginx 转发到 demo 环境:
location ^~ /testwx/ {
proxy_pass https://demo.xxx.cn/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host demo.xxx.cn;
proxy_set_header X-Forwarded-Proto https;
proxy_ssl_server_name on;
proxy_ssl_name demo.xxx.cn;
proxy_redirect off;
}这样微信看到的 redirect_uri 是:
http://wx.xxx.cn/testwx/r/wxAuth/public/oauth2/loginByCode这个域名属于已配置的网页授权域名。
而 Nginx 实际转发到:
https://demo.xxx.cn/r/wxAuth/public/oauth2/loginByCode这样 demo 服务不需要直接占用公众号后台的网页授权域名名额。
六、Nginx 中转时的关键点
1. location 建议使用 ^~
建议使用:
location ^~ /testwx/ {
...
}而不是普通的:
location /testwx/ {
...
}因为有些 Nginx 配置里会包含其他正则 location、rewrite 或面板自动生成的配置。使用 ^~ 可以明确告诉 Nginx:只要路径以 /testwx/ 开头,就优先使用这个 location,不再继续匹配后面的正则 location。
2. proxy_pass 末尾的 / 很重要
下面这个配置:
location ^~ /testwx/ {
proxy_pass https://demo.xxx.cn/;
}会把:
/testwx/r/wxAuth/public/oauth2/loginByCode转发成:
/r/wxAuth/public/oauth2/loginByCode也就是自动去掉 /testwx/ 前缀。
如果写成:
proxy_pass https://demo.xxx.cn;则可能会把 /testwx/ 原样带给上游。
所以这里要根据后端实际接口路径决定是否保留前缀。
3. Host 建议设置成上游域名
如果上游是:
demo.xxx.cn则建议:
proxy_set_header Host demo.xxx.cn;这样上游 Nginx 或后端应用会认为请求是访问 demo 域名,而不是访问统一授权域名。
4. HTTPS 上游要开启 SNI
如果代理到 HTTPS 上游,建议加:
proxy_ssl_server_name on;
proxy_ssl_name demo.xxx.cn;否则某些 HTTPS 站点可能因为 SNI 不正确导致证书或路由异常。
5. 不要一开始就加太多 forwarded header
实际排查中,有些上游会因为:
X-Real-IP
X-Forwarded-For
X-Forwarded-Host
X-Forwarded-Prefix这些 header 触发安全规则或网关判断,导致返回 403。
所以建议先使用最小配置跑通:
location ^~ /testwx/ {
proxy_pass https://demo.xxx.cn/;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host demo.xxx.cn;
proxy_set_header X-Forwarded-Proto https;
proxy_ssl_server_name on;
proxy_ssl_name demo.xxx.cn;
proxy_redirect off;
}跑通后,再按需逐步加回真实 IP 相关 header。
七、业务系统需要注意什么
统一授权域名方案不是只改 Nginx,还需要业务系统配合。
1. redirect_uri 要改成统一授权域名
原来可能是:
https://demo.xxx.cn/r/wxAuth/public/oauth2/loginByCode现在应该改成:
http://wx.xxx.cn/testwx/r/wxAuth/public/oauth2/loginByCode或者长期方案:
https://wx-auth.xxx.cn/demo/oauth/callback2. 回跳前端时要使用绝对地址
授权完成后,后端通常会 302 跳回前端页面。
这里不要使用相对路径:
/xxx?token=xxx因为当前浏览器地址可能还在:
http://wx.xxx.cn/testwx/...如果使用相对路径,用户可能会继续停留在统一授权域名下。
应该使用绝对地址:
https://demo.xxx.cn/xxx?token=xxx更好的方式是从配置或 state 中获取最终跳转地址。
3. 不要把微信 code 直接暴露给前端
微信回调拿到的 code 应该由后端消费。
推荐流程是:
微信 code
↓
后端换 openid
↓
后端生成内部 token 或临时票据
↓
302 跳回前端不要把微信 code 直接传给前端,再由前端去换 openid。
4. appsecret 应该集中管理
如果多个环境都需要获取 openid,最好不要让每个环境都保存正式公众号的 appsecret。
中长期建议是:
wx-auth 服务统一保存 appid/appsecret
业务系统只接收 wx-auth 返回的内部身份结果这样更安全,也更容易审计。
八、一次实际排查经验
在一次接入中,我们遇到过这样的情况:
http://wx.xxx.cn/testwx/r/wxAuth/public/oauth2/loginByCode?code=test&state=test最开始返回:
HTTP/1.1 403 Forbidden
Server: nginx直接访问上游 demo:
https://demo.xxx.cn/r/wxAuth/public/oauth2/loginByCode?code=test&state=test返回的是:
HTTP/1.1 400
{"message":"invalid code"}这说明 demo 接口本身是通的,400 invalid code 是正常业务错误,因为测试时传的是假 code。
后来通过增加测试 location:
location ^~ /testwx/ {
return 200 "hit testwx prefix: $uri\n";
}确认真实路径可以命中 /testwx/。
之后再逐步恢复 proxy 配置,发现最小代理配置可以正常返回上游的 400 JSON,说明链路已打通。
这个过程说明,排查微信授权中转问题时,不要一开始就怀疑微信。应该按顺序确认:
1. 微信是否回调到了统一域名
2. Nginx server_name 是否命中
3. location 是否命中
4. proxy_pass 路径是否正确
5. 上游接口是否能直接访问
6. header 是否触发上游安全规则
7. 最终 code 是否真实有效九、推荐最终形态
短期可以使用:
wx.xxx.cn/testwx/作为测试环境的微信网页授权中转路径。
中长期更推荐抽象成独立服务:
wx-auth.xxx.cn统一处理:
/oauth/start
/oauth/callback
/openid/resolve
/token/exchange这样业务系统只关心自己的登录态,不需要直接理解微信 OAuth 的所有细节。
推荐架构如下:
公众号后台网页授权域名:
wx-auth.xxx.cn
生产:
https://wx-auth.xxx.cn/prod/oauth/callback
pre:
https://wx-auth.xxx.cn/pre/oauth/callback
demo:
https://wx-auth.xxx.cn/demo/oauth/callback
测试:
https://wx-auth.xxx.cn/test/oauth/callback所有环境共享一个授权入口,但通过 state 或路径区分来源环境。
十、总结
统一微信授权域名的核心价值,不只是节省公众号后台的网页授权域名名额。
它更重要的意义在于:
- 收敛微信 OAuth 入口;
- 统一管理 appid 和 appsecret;
- 降低多环境接入复杂度;
- 避免测试、pre、生产各自维护一套授权逻辑;
- 方便记录授权日志和排查问题;
- 提升安全性和可维护性。
对于只有一个简单公众号页面的系统,直接配置业务域名即可。
但只要系统开始出现多环境、多业务、多前端、多后端,统一授权域名就非常值得提前设计。
它是一个小改动,但能避免后面很多混乱。
最后编辑:张三 更新时间:2026-06-12 17:12