公众号网页授权统一域名方案

微信公众号网页授权:使用统一授权域名解决多环境 redirect_uri 限制
使用统一微信授权域名接入公众号网页授权

一、背景

在微信公众号网页授权场景中,获取 openid 通常需要经过微信 OAuth 流程:

  1. 用户在微信内打开业务页面;
  2. 业务系统生成微信授权地址;
  3. 微信授权完成后回调 redirect_uri
  4. 后端拿到 code
  5. 后端使用 code 换取 openid;
  6. 系统生成自己的登录态或 token;
  7. 最后跳回前端页面。

这个流程本身并不复杂,但在真实业务中经常会遇到一个问题:公众号后台可配置的网页授权域名数量有限,而我们的系统往往有生产、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

因为这些都是不同域名。

如果系统只有一个生产环境,直接配置生产域名即可。但一旦系统存在多个环境,直接把每个环境都配置到公众号后台,就会带来几个问题:

  1. 域名名额不够;
  2. 测试环境和生产公众号绑定过深;
  3. appid、appsecret 容易分散到多个系统;
  4. 授权逻辑散落在不同环境,后期维护困难;
  5. 回调、登录态、跳转逻辑容易不一致。

统一授权域名的价值,就是把微信授权入口收敛起来。

三、推荐架构

推荐使用一个独立域名作为统一微信授权入口:

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 非常关键。

因为微信回调时,只会把 codestate 带回来:

https://wx-auth.xxx.cn/oauth/callback?code=xxx&state=xxx

所以我们需要通过 state 知道:

  1. 用户来自哪个环境;
  2. 用户授权前想访问哪个页面;
  3. 授权完成后应该跳回哪里;
  4. 是否存在 CSRF 风险;
  5. 这次授权请求是否过期。

一个比较实用的 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

它负责:

  1. 生成微信 OAuth URL;
  2. 接收微信回调;
  3. 使用 code 换 openid;
  4. 维护 openid 与业务用户的映射;
  5. 根据 state 跳回不同环境;
  6. 统一处理异常和日志。

优点是架构清晰、职责单一、安全性高。

适合中长期方案。

方案二: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/callback

2. 回跳前端时要使用绝对地址

授权完成后,后端通常会 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 或路径区分来源环境。


十、总结

统一微信授权域名的核心价值,不只是节省公众号后台的网页授权域名名额。

它更重要的意义在于:

  1. 收敛微信 OAuth 入口;
  2. 统一管理 appid 和 appsecret;
  3. 降低多环境接入复杂度;
  4. 避免测试、pre、生产各自维护一套授权逻辑;
  5. 方便记录授权日志和排查问题;
  6. 提升安全性和可维护性。

对于只有一个简单公众号页面的系统,直接配置业务域名即可。

但只要系统开始出现多环境、多业务、多前端、多后端,统一授权域名就非常值得提前设计。

它是一个小改动,但能避免后面很多混乱。

作者:张三  创建时间:2026-06-12 17:06
最后编辑:张三  更新时间:2026-06-12 17:12