From 6c2e095196a8b1f15538cc88cbb88d7f9eb84c72 Mon Sep 17 00:00:00 2001 From: zzh <1368735060@qq.com> Date: Sat, 4 Jul 2026 12:30:42 +0800 Subject: [PATCH] =?UTF-8?q?fix(=E5=BE=AE=E4=BF=A1):=20=E9=AA=8C=E7=AD=BE?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E4=BB=85=E4=BF=9D=E7=95=99=20GET=20=E5=B9=B6?= =?UTF-8?q?=E5=8E=9F=E6=A0=B7=E8=BF=94=E5=9B=9E=20echostr?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /apiwechat/verification 移除 HEAD/POST,仅保留 GET 验签 - 验签通过后从 Query 原样返回 echostr 纯文本 - 扫码回调测试改走 /wechat/mp/{appId} Co-authored-by: Cursor --- .../WeChatRouteIntegrationTests.cs | 26 ++++++-- .../WeChatScanLoginIntegrationTests.cs | 2 +- .../Auth/AuthService.WeChatMp.cs | 64 +++++-------------- .../WeChat/WeChatMpCallbackSupport.cs | 16 +++-- 4 files changed, 47 insertions(+), 61 deletions(-) diff --git a/src/SimpleAdmin.Api.IntegrationTests/WeChatRouteIntegrationTests.cs b/src/SimpleAdmin.Api.IntegrationTests/WeChatRouteIntegrationTests.cs index 8d0ba6e..2198035 100644 --- a/src/SimpleAdmin.Api.IntegrationTests/WeChatRouteIntegrationTests.cs +++ b/src/SimpleAdmin.Api.IntegrationTests/WeChatRouteIntegrationTests.cs @@ -16,13 +16,12 @@ namespace SimpleAdmin.Api.IntegrationTests; public class WeChatRouteIntegrationTests { [Fact] - public async Task WeChatLegacyVerification_ShouldRegisterGetAndPostMethods() + public async Task WeChatLegacyVerification_ShouldRegisterGetOnly() { await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: ConfigureWeChatFallback); var methods = host.GetHttpMethods("apiwechat/verification"); - methods.Should().Contain("GET"); - methods.Should().Contain("POST"); + methods.Should().BeEquivalentTo(["GET"]); } [Fact] @@ -61,14 +60,27 @@ public class WeChatRouteIntegrationTests } [Fact] - public async Task WeChatValidateLegacy_Head_ShouldReturn200() + public async Task WeChatValidateLegacy_ShouldReturnEchostrAsPlainTextWithoutJsonWrapper() { - await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: ConfigureWeChatFallback); + await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services => + { + ConfigureWeChatFallback(services); + services.PostConfigure(options => options.Token = "test-token"); + }); - var request = new HttpRequestMessage(HttpMethod.Head, "/apiwechat/verification"); - var response = await host.Client.SendAsync(request); + const string timestamp = "1400000000"; + const string nonce = "nonce"; + const string echostr = "5938472619053847261"; + var signature = SignWeChat("test-token", timestamp, nonce); + + var response = await host.Client.GetAsync( + $"/apiwechat/verification?signature={signature}×tamp={timestamp}&nonce={nonce}&echostr={echostr}"); response.StatusCode.Should().Be(HttpStatusCode.OK); + response.Content.Headers.ContentType?.MediaType.Should().Be("text/plain"); + var body = await response.Content.ReadAsStringAsync(); + body.Should().Be(echostr); + body.Should().NotStartWith("{"); } private static void ConfigureWeChatFallback(IServiceCollection services) diff --git a/src/SimpleAdmin.Api.IntegrationTests/WeChatScanLoginIntegrationTests.cs b/src/SimpleAdmin.Api.IntegrationTests/WeChatScanLoginIntegrationTests.cs index eb5f2a0..808e528 100644 --- a/src/SimpleAdmin.Api.IntegrationTests/WeChatScanLoginIntegrationTests.cs +++ b/src/SimpleAdmin.Api.IntegrationTests/WeChatScanLoginIntegrationTests.cs @@ -61,7 +61,7 @@ public class WeChatScanLoginIntegrationTests using var postContent = new StringContent(scanXml, Encoding.UTF8, "text/xml"); var postResponse = await host.Client.PostAsync( - $"/apiwechat/verification?signature={signature}×tamp={timestamp}&nonce={nonce}", + $"/wechat/mp/wx-test?signature={signature}×tamp={timestamp}&nonce={nonce}", postContent); postResponse.StatusCode.Should().Be(HttpStatusCode.OK); diff --git a/src/SimpleAdmin.Api/Auth/AuthService.WeChatMp.cs b/src/SimpleAdmin.Api/Auth/AuthService.WeChatMp.cs index ca1ec56..f805902 100644 --- a/src/SimpleAdmin.Api/Auth/AuthService.WeChatMp.cs +++ b/src/SimpleAdmin.Api/Auth/AuthService.WeChatMp.cs @@ -23,42 +23,36 @@ public partial class AuthService LazyServiceProvider.GetRequiredService>(); /// - /// 微信公众号接入验签(GET) + /// 微信公众号接入验签,验签通过后原样返回 echostr。 /// [AllowAnonymous] [HttpGet("/apiwechat/verification")] - [HttpHead("/apiwechat/verification")] [Produces("text/plain")] [ApiExplorerSettings(GroupName = "WeChat")] - public async Task WeChatValidateLegacyAsync( + public Task WeChatValidateLegacyAsync( [FromQuery] string? signature, [FromQuery] string? timestamp, [FromQuery] string? nonce, [FromQuery] string? echostr, - CancellationToken cancellationToken = default) - { - if (string.Equals( - httpContextAccessor.HttpContext?.Request.Method, - HttpMethods.Head, - StringComparison.OrdinalIgnoreCase)) - { - return WeChatMpCallbackSupport.HeadOk(); - } - - return await WeChatValidateCoreAsync(signature, timestamp, nonce, echostr, cancellationToken); - } + CancellationToken cancellationToken = default) => + WeChatValidateCoreAsync(signature, timestamp, nonce, cancellationToken); private async Task WeChatValidateCoreAsync( string? signature, string? timestamp, string? nonce, - string? echostr, CancellationToken cancellationToken) { + var request = httpContextAccessor.HttpContext?.Request; + if (request == null) + { + return WeChatMpCallbackSupport.PlainError(); + } + try { var options = await WeChatMpConfigProvider.GetAsync(cancellationToken); - return WeChatMpCallbackSupport.Validate(options, signature, timestamp, nonce, echostr); + return WeChatMpCallbackSupport.Validate(options, request, signature, timestamp, nonce); } catch (Exception ex) { @@ -68,7 +62,7 @@ public partial class AuthService } /// - /// 微信服务器接入验签。 + /// 微信服务器接入验签,验签通过后原样返回 echostr。 /// [AllowAnonymous] [HttpGet("/wechat/mp/{appId}")] @@ -82,39 +76,11 @@ public partial class AuthService [FromQuery] string? echostr, CancellationToken cancellationToken = default) { + var request = httpContextAccessor.HttpContext?.Request + ?? throw new BusinessException("无法读取微信验签请求"); var options = await WeChatMpConfigProvider.GetAsync(cancellationToken); WeChatMpCallbackSupport.EnsureAppId(options, appId); - return WeChatMpCallbackSupport.Validate(options, signature, timestamp, nonce, echostr); - } - - /// - /// 微信公众号消息/事件回调(POST) - /// - [AllowAnonymous] - [HttpPost("/apiwechat/verification")] - [Consumes("text/xml", "application/xml", "text/plain")] - [Produces("application/xml")] - [ApiExplorerSettings(GroupName = "WeChat")] - public async Task WeChatMessageLegacyAsync( - [FromQuery] string signature, - [FromQuery] string timestamp, - [FromQuery] string nonce, - [FromQuery(Name = "encrypt_type")] string? encryptType, - CancellationToken cancellationToken = default) - { - var options = await WeChatMpConfigProvider.GetAsync(cancellationToken); - var request = httpContextAccessor.HttpContext?.Request - ?? throw new BusinessException("无法读取微信回调请求"); - return await WeChatMpCallbackSupport.HandleMessageAsync( - options, - WeChatEventHandler, - request, - WeChatLogger, - signature, - timestamp, - nonce, - encryptType, - cancellationToken); + return WeChatMpCallbackSupport.Validate(options, request, signature, timestamp, nonce); } /// diff --git a/src/SimpleAdmin.Api/WeChat/WeChatMpCallbackSupport.cs b/src/SimpleAdmin.Api/WeChat/WeChatMpCallbackSupport.cs index d5ecbc2..f3c8912 100644 --- a/src/SimpleAdmin.Api/WeChat/WeChatMpCallbackSupport.cs +++ b/src/SimpleAdmin.Api/WeChat/WeChatMpCallbackSupport.cs @@ -9,10 +9,10 @@ internal static class WeChatMpCallbackSupport { public static ContentResult Validate( WeChatMpOptions options, + HttpRequest request, string? signature, string? timestamp, - string? nonce, - string? echostr) + string? nonce) { // 微信服务器要求 HTTP 200 + 纯文本 echostr,不可抛 BusinessException(会变成 JSON 非 200) if (string.IsNullOrWhiteSpace(options.Token)) @@ -20,16 +20,21 @@ internal static class WeChatMpCallbackSupport return PlainError(); } + var echostr = request.Query.TryGetValue("echostr", out var echostrValue) + ? echostrValue.ToString() + : string.Empty; + if (string.IsNullOrWhiteSpace(signature) || string.IsNullOrWhiteSpace(timestamp) || string.IsNullOrWhiteSpace(nonce) - || string.IsNullOrWhiteSpace(echostr)) + || string.IsNullOrEmpty(echostr)) { return PlainError(); } var valid = WeChatSignatureHelper.Validate(options.Token, timestamp, nonce, signature); - return PlainText(valid ? echostr : "error"); + // 验签通过后原样返回 echostr,不可包 JSON、不可加引号或换行 + return valid ? EchoStr(echostr) : PlainError(); } /// 微信或探活可能发 HEAD,须返回 200(WMS/Spring 默认支持)。 @@ -95,6 +100,9 @@ internal static class WeChatMpCallbackSupport } } + private static ContentResult EchoStr(string echostr) => + new() { Content = echostr, ContentType = "text/plain", StatusCode = StatusCodes.Status200OK }; + private static ContentResult PlainText(string content) => new() { Content = content, ContentType = "text/plain; charset=utf-8", StatusCode = StatusCodes.Status200OK };