🎨 【微信支付】微信支付api-host-url配置反向代理路径前缀时会导致v3的签名异常#3968
🎨 【微信支付】微信支付api-host-url配置反向代理路径前缀时会导致v3的签名异常#3968shuiyihan12 wants to merge 27 commits intobinarywang:developfrom
Conversation
🤖 Augment PR SummarySummary: This PR improves WeChat Pay API reverse-proxy support by separating the proxy host and an optional path-prefix, and updating V3 signing/verification to account for that prefix. Changes:
🤖 Was this summary useful? React with 👍 or 👎 |
| } | ||
|
|
||
| WxPayV3HttpClientBuilder wxPayV3HttpClientBuilder = WxPayV3HttpClientBuilder.create() | ||
| .withSignUriStripPrefix(this.getApiHostUrlPath()) |
There was a problem hiding this comment.
weixin-java-pay/.../WxPayConfig.java:435 If a user keeps the legacy style apiHostUrl containing a path prefix (e.g., http://host/api-weixin) but leaves apiHostUrlPath empty, withSignUriStripPrefix stays blank and V3 requests may still sign "/api-weixin/...", so signatures can still fail behind rewrite proxies.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
| if (rawPath == null || rawPath.isEmpty() || signUriStripPrefix == null) { | ||
| return rawPath; | ||
| } | ||
| if (!rawPath.startsWith(signUriStripPrefix)) { |
There was a problem hiding this comment.
weixin-java-pay/.../WxPayCredentials.java:123 stripPathPrefix uses rawPath.startsWith(signUriStripPrefix), which can strip unintended paths when the prefix is a partial segment (e.g., prefix /api also matches /api2/...), leading to incorrect canonical URLs and signature failures.
Severity: medium
🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.
There was a problem hiding this comment.
Pull request overview
该 PR 针对微信支付在“反向代理带路径前缀”场景下,V3 签名/验签依赖请求 URI Path 导致的签名不一致问题,引入“host 与路径前缀分离配置”,并在签名串构造时对代理前缀进行剥离,从而避免 SIGN_ERROR。
Changes:
- 新增
apiHostUrlPath(代理入口路径前缀)配置,并提供getApiHostWithPathPrefix()作为统一的请求基础地址拼接结果。 - V3 签名串构造时支持剥离代理 path 前缀(
signUriStripPrefix),并在 V3 HttpClient/证书更新校验链路中贯通该配置。 - Spring Boot Starter / Multi Starter / Solon 插件补齐配置项映射、README 示例与相关测试。
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| weixin-java-pay/src/test/java/com/github/binarywang/wxpay/config/WxPayConfigTest.java | 增加 apiHostUrl/apiHostUrlPath 规范化与拼接的测试用例 |
| weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/auth/WxPayCredentials.java | V3 签名 message 构造时增加“Path 前缀剥离”能力 |
| weixin-java-pay/src/main/java/com/github/binarywang/wxpay/v3/WxPayV3HttpClientBuilder.java | Builder 支持注入 signUriStripPrefix 并同步到 Credentials |
| weixin-java-pay/src/main/java/com/github/binarywang/wxpay/service/impl/BaseWxPayServiceImpl.java | getPayBaseUrl() 改为返回 host+pathPrefix,覆盖 v2/v3 URL 生成 |
| weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/WxPayConfig.java | 新增 apiHostUrlPath、规范化 getter、以及 getApiHostWithPathPrefix();初始化 V3 client 时透传剥离前缀 |
| weixin-java-pay/src/main/java/com/github/binarywang/wxpay/config/VerifierBuilder.java | 从 payBaseUrl 解析 path 作为签名前缀剥离参数,保证证书下载签名一致 |
| spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/.../WxPayProperties.java | Starter 配置项新增 apiHostUrlPath |
| spring-boot-starters/wx-java-pay-spring-boot-starter/src/main/java/.../WxPayAutoConfiguration.java | 将 apiHostUrlPath 映射到 WxPayConfig |
| spring-boot-starters/wx-java-pay-spring-boot-starter/README.md | 文档增加 apiHostUrl/apiHostUrlPath 示例 |
| spring-boot-starters/wx-java-pay-multi-spring-boot-starter/src/test/java/.../WxPayMultiServicesTest.java | 多配置场景补充 apiHostUrlPath 绑定断言 |
| spring-boot-starters/wx-java-pay-multi-spring-boot-starter/src/main/java/.../WxPayMultiServicesImpl.java | 多配置构建 WxPayConfig 时注入 apiHostUrlPath |
| spring-boot-starters/wx-java-pay-multi-spring-boot-starter/src/main/java/.../WxPaySingleProperties.java | 单配置属性新增 apiHostUrlPath |
| spring-boot-starters/wx-java-pay-multi-spring-boot-starter/README.md | 多配置文档补充 apiHostUrlPath 说明 |
| solon-plugins/wx-java-pay-solon-plugin/src/main/java/.../WxPayProperties.java | Solon 插件属性新增 apiHostUrlPath |
| solon-plugins/wx-java-pay-solon-plugin/src/main/java/.../WxPayAutoConfiguration.java | Solon 自动配置注入 apiHostUrlPath |
| solon-plugins/wx-java-pay-solon-plugin/README.md | Solon 文档增加 apiHostUrl/apiHostUrlPath 示例 |
| if (!rawPath.startsWith(signUriStripPrefix)) { | ||
| return rawPath; | ||
| } | ||
| String stripped = rawPath.substring(signUriStripPrefix.length()); |
There was a problem hiding this comment.
stripPathPrefix 仅用 rawPath.startsWith(signUriStripPrefix) 判断是否需要剥离前缀,可能发生“部分前缀误匹配”:例如配置前缀为 "/api" 时,请求路径 "/api-weixin/v3/..." 也会被误剥离,导致签名串的 Path 变成"/-weixin/v3/..." 从而签名必然异常。建议改为仅在 rawPath 等于前缀或以 前缀 + "/" 开头时才剥离,避免跨 segment 的误匹配。
| if (!rawPath.startsWith(signUriStripPrefix)) { | |
| return rawPath; | |
| } | |
| String stripped = rawPath.substring(signUriStripPrefix.length()); | |
| String normalizedPrefix = signUriStripPrefix; | |
| if (normalizedPrefix.length() > 1 && normalizedPrefix.endsWith("/")) { | |
| normalizedPrefix = normalizedPrefix.substring(0, normalizedPrefix.length() - 1); | |
| } | |
| boolean exactMatch = rawPath.equals(normalizedPrefix); | |
| boolean segmentMatch = rawPath.startsWith(normalizedPrefix + "/"); | |
| if (!exactMatch && !segmentMatch) { | |
| return rawPath; | |
| } | |
| String stripped = rawPath.substring(normalizedPrefix.length()); |
| if (rawPath == null || rawPath.isEmpty() || signUriStripPrefix == null) { | ||
| return rawPath; | ||
| } | ||
| if (!rawPath.startsWith(signUriStripPrefix)) { | ||
| return rawPath; | ||
| } |
There was a problem hiding this comment.
新增的签名 Path 前缀剥离逻辑目前缺少单元测试覆盖。建议至少补充边界 case:前缀等于整个 path、前缀后必须是/才剥离、以及不会误匹配到更长的同前缀路径(如"/api"不应匹配"/api-weixin"),以避免签名回归。
| if (rawPath == null || rawPath.isEmpty() || signUriStripPrefix == null) { | |
| return rawPath; | |
| } | |
| if (!rawPath.startsWith(signUriStripPrefix)) { | |
| return rawPath; | |
| } | |
| if (rawPath == null || rawPath.isEmpty() || signUriStripPrefix == null || signUriStripPrefix.isEmpty()) { | |
| return rawPath; | |
| } | |
| if (!rawPath.startsWith(signUriStripPrefix)) { | |
| return rawPath; | |
| } | |
| if (rawPath.length() > signUriStripPrefix.length() | |
| && rawPath.charAt(signUriStripPrefix.length()) != '/') { | |
| return rawPath; | |
| } |
简要描述
微信支付api-host-url配置反向代理路径前缀时会导致v3的签名异常
模块版本情况
详细描述
当前支持的反向代理配置:
异常定位:
App(WxJava) --签名路径:/api-weixin/v3/...-->Proxy(127.0.0.1:3338) --转发/重写-->
WeChatPay(校验路径通常是 /v3/...)=> 路径不一致 => SIGN_ERROR
之后改造:
当前调整后的代码已在生产中应用