feat(wechat): 公众号配置改从 t_system_other_config 读取,支持后台其他配置维护
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d168a21e96
commit
5c6c4f6e48
31
sql/migrate_wechat_mp_other_config.sql
Normal file
31
sql/migrate_wechat_mp_other_config.sql
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
-- 微信公众号配置(t_system_other_config)
|
||||||
|
-- 字段映射:AppKey=AppId,AppSecret=Secret,Remark=Token,BaseUrl=OAuthRedirectBase
|
||||||
|
-- 请在管理端「系统 → 其他配置」填写真实 AppId / AppSecret / Token,勿将密钥提交到 Git。
|
||||||
|
|
||||||
|
INSERT INTO `t_system_other_config` (
|
||||||
|
`ConfigCode`,
|
||||||
|
`ConfigName`,
|
||||||
|
`Enabled`,
|
||||||
|
`BaseUrl`,
|
||||||
|
`AppKey`,
|
||||||
|
`AppSecret`,
|
||||||
|
`Remark`,
|
||||||
|
`TimeoutSeconds`,
|
||||||
|
`IsDeleted`,
|
||||||
|
`CreateUserId`
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
'wechat_mp',
|
||||||
|
'微信公众号',
|
||||||
|
1,
|
||||||
|
'https://oms.nanafa.com',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
30,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
FROM DUAL
|
||||||
|
WHERE NOT EXISTS (
|
||||||
|
SELECT 1 FROM `t_system_other_config` WHERE `ConfigCode` = 'wechat_mp' AND `IsDeleted` = 0
|
||||||
|
);
|
||||||
@ -163,6 +163,23 @@ public class SystemOtherConfigService : ApplicationService
|
|||||||
throw new BusinessException("轨迹服务 AppSecret 不能为空");
|
throw new BusinessException("轨迹服务 AppSecret 不能为空");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else if (string.Equals(configCode, SystemOtherConfigCodes.WeChatMp, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(input.AppKey))
|
||||||
|
{
|
||||||
|
throw new BusinessException("微信公众号 AppId 不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(input.Remark))
|
||||||
|
{
|
||||||
|
throw new BusinessException("微信公众号 Token 不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (excludeId == null && ShouldKeepExistingSecret(input.AppSecret))
|
||||||
|
{
|
||||||
|
throw new BusinessException("微信公众号 AppSecret 不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SystemOtherConfigOutput MapOutput(SystemOtherConfig entity, bool maskSecret)
|
private static SystemOtherConfigOutput MapOutput(SystemOtherConfig entity, bool maskSecret)
|
||||||
|
|||||||
@ -9,4 +9,9 @@ public static class SystemOtherConfigCodes
|
|||||||
/// 小包轨迹服务配置。
|
/// 小包轨迹服务配置。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public const string TrackService = "track_service";
|
public const string TrackService = "track_service";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 微信公众号配置(AppKey=AppId,AppSecret=Secret,Remark=Token,BaseUrl=OAuthRedirectBase)。
|
||||||
|
/// </summary>
|
||||||
|
public const string WeChatMp = "wechat_mp";
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,11 @@ using System.Security.Cryptography;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using FluentAssertions;
|
using FluentAssertions;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Moq;
|
||||||
|
using Oms.Domain.SystemAggregate.Consts;
|
||||||
|
using Oms.Domain.SystemAggregate.Entities;
|
||||||
|
using Oms.Domain.SystemAggregate.Repositories;
|
||||||
using SimpleAdmin.Api.IntegrationTests.Support;
|
using SimpleAdmin.Api.IntegrationTests.Support;
|
||||||
using SimpleAdmin.Api.WeChat.Options;
|
using SimpleAdmin.Api.WeChat.Options;
|
||||||
|
|
||||||
@ -13,14 +18,7 @@ public class WeChatRouteIntegrationTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task WeChatRoutes_ShouldBeRegisteredOnAuthService()
|
public async Task WeChatRoutes_ShouldBeRegisteredOnAuthService()
|
||||||
{
|
{
|
||||||
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services =>
|
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: ConfigureWeChatFallback);
|
||||||
{
|
|
||||||
services.PostConfigure<WeChatMpOptions>(options =>
|
|
||||||
{
|
|
||||||
options.Token = "test-token";
|
|
||||||
options.AppId = "wx-test";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
host.RoutePatterns.Should().Contain("apiwechat/verification");
|
host.RoutePatterns.Should().Contain("apiwechat/verification");
|
||||||
host.RoutePatterns.Should().Contain("wechat/mp/{appId}");
|
host.RoutePatterns.Should().Contain("wechat/mp/{appId}");
|
||||||
@ -32,6 +30,7 @@ public class WeChatRouteIntegrationTests
|
|||||||
{
|
{
|
||||||
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services =>
|
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services =>
|
||||||
{
|
{
|
||||||
|
ConfigureWeChatFallback(services);
|
||||||
services.PostConfigure<WeChatMpOptions>(options => options.Token = "test-token");
|
services.PostConfigure<WeChatMpOptions>(options => options.Token = "test-token");
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -47,6 +46,22 @@ public class WeChatRouteIntegrationTests
|
|||||||
(await response.Content.ReadAsStringAsync()).Should().Be(echostr);
|
(await response.Content.ReadAsStringAsync()).Should().Be(echostr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void ConfigureWeChatFallback(IServiceCollection services)
|
||||||
|
{
|
||||||
|
services.PostConfigure<WeChatMpOptions>(options =>
|
||||||
|
{
|
||||||
|
options.Token = "test-token";
|
||||||
|
options.AppId = "wx-test";
|
||||||
|
});
|
||||||
|
|
||||||
|
var repository = new Mock<ISystemOtherConfigRepository>();
|
||||||
|
repository
|
||||||
|
.Setup(x => x.GetByConfigCodeAsync(SystemOtherConfigCodes.WeChatMp, It.IsAny<CancellationToken>()))
|
||||||
|
.ReturnsAsync((SystemOtherConfig?)null);
|
||||||
|
services.RemoveAll<ISystemOtherConfigRepository>();
|
||||||
|
services.AddSingleton(repository.Object);
|
||||||
|
}
|
||||||
|
|
||||||
private static string SignWeChat(string token, string timestamp, string nonce)
|
private static string SignWeChat(string token, string timestamp, string nonce)
|
||||||
{
|
{
|
||||||
var parts = new[] { token, timestamp, nonce }.OrderBy(x => x, StringComparer.Ordinal).ToArray();
|
var parts = new[] { token, timestamp, nonce }.OrderBy(x => x, StringComparer.Ordinal).ToArray();
|
||||||
|
|||||||
@ -2,18 +2,16 @@ using Fake.Application;
|
|||||||
using Fake.Domain.Exceptions;
|
using Fake.Domain.Exceptions;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using SimpleAdmin.Api.WeChat;
|
using SimpleAdmin.Api.WeChat;
|
||||||
using SimpleAdmin.Api.WeChat.Dtos;
|
using SimpleAdmin.Api.WeChat.Dtos;
|
||||||
using SimpleAdmin.Api.WeChat.Options;
|
|
||||||
using SimpleAdmin.Api.WeChat.Services;
|
using SimpleAdmin.Api.WeChat.Services;
|
||||||
|
|
||||||
namespace SimpleAdmin.Api.Auth;
|
namespace SimpleAdmin.Api.Auth;
|
||||||
|
|
||||||
public partial class AuthService
|
public partial class AuthService
|
||||||
{
|
{
|
||||||
private IOptions<WeChatMpOptions> WeChatMpOptions =>
|
private IWeChatMpConfigProvider WeChatMpConfigProvider =>
|
||||||
LazyServiceProvider.GetRequiredService<IOptions<WeChatMpOptions>>();
|
LazyServiceProvider.GetRequiredService<IWeChatMpConfigProvider>();
|
||||||
|
|
||||||
private IWeChatMpApiClient WeChatMpApiClient =>
|
private IWeChatMpApiClient WeChatMpApiClient =>
|
||||||
LazyServiceProvider.GetRequiredService<IWeChatMpApiClient>();
|
LazyServiceProvider.GetRequiredService<IWeChatMpApiClient>();
|
||||||
@ -31,12 +29,16 @@ public partial class AuthService
|
|||||||
[HttpGet("/apiwechat/verification")]
|
[HttpGet("/apiwechat/verification")]
|
||||||
[Produces("text/plain")]
|
[Produces("text/plain")]
|
||||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||||
public ContentResult WeChatValidateLegacyAsync(
|
public async Task<ContentResult> WeChatValidateLegacyAsync(
|
||||||
[FromQuery] string? signature,
|
[FromQuery] string? signature,
|
||||||
[FromQuery] string? timestamp,
|
[FromQuery] string? timestamp,
|
||||||
[FromQuery] string? nonce,
|
[FromQuery] string? nonce,
|
||||||
[FromQuery] string? echostr)
|
[FromQuery] string? echostr,
|
||||||
=> WeChatMpCallbackSupport.Validate(WeChatMpOptions, signature, timestamp, nonce, echostr);
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var options = await WeChatMpConfigProvider.GetAsync(cancellationToken);
|
||||||
|
return WeChatMpCallbackSupport.Validate(options, signature, timestamp, nonce, echostr);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 微信服务器接入验签。
|
/// 微信服务器接入验签。
|
||||||
@ -45,15 +47,17 @@ public partial class AuthService
|
|||||||
[HttpGet("/wechat/mp/{appId}")]
|
[HttpGet("/wechat/mp/{appId}")]
|
||||||
[Produces("text/plain")]
|
[Produces("text/plain")]
|
||||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||||
public ContentResult WeChatValidateAsync(
|
public async Task<ContentResult> WeChatValidateAsync(
|
||||||
[FromRoute] string appId,
|
[FromRoute] string appId,
|
||||||
[FromQuery] string? signature,
|
[FromQuery] string? signature,
|
||||||
[FromQuery] string? timestamp,
|
[FromQuery] string? timestamp,
|
||||||
[FromQuery] string? nonce,
|
[FromQuery] string? nonce,
|
||||||
[FromQuery] string? echostr)
|
[FromQuery] string? echostr,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
WeChatMpCallbackSupport.EnsureAppId(WeChatMpOptions, appId);
|
var options = await WeChatMpConfigProvider.GetAsync(cancellationToken);
|
||||||
return WeChatMpCallbackSupport.Validate(WeChatMpOptions, signature, timestamp, nonce, echostr);
|
WeChatMpCallbackSupport.EnsureAppId(options, appId);
|
||||||
|
return WeChatMpCallbackSupport.Validate(options, signature, timestamp, nonce, echostr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -64,17 +68,18 @@ public partial class AuthService
|
|||||||
[Consumes("text/xml", "application/xml", "text/plain")]
|
[Consumes("text/xml", "application/xml", "text/plain")]
|
||||||
[Produces("application/xml")]
|
[Produces("application/xml")]
|
||||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||||
public Task<ContentResult> WeChatMessageLegacyAsync(
|
public async Task<ContentResult> WeChatMessageLegacyAsync(
|
||||||
[FromQuery] string signature,
|
[FromQuery] string signature,
|
||||||
[FromQuery] string timestamp,
|
[FromQuery] string timestamp,
|
||||||
[FromQuery] string nonce,
|
[FromQuery] string nonce,
|
||||||
[FromQuery(Name = "encrypt_type")] string? encryptType,
|
[FromQuery(Name = "encrypt_type")] string? encryptType,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
|
var options = await WeChatMpConfigProvider.GetAsync(cancellationToken);
|
||||||
var request = httpContextAccessor.HttpContext?.Request
|
var request = httpContextAccessor.HttpContext?.Request
|
||||||
?? throw new BusinessException("无法读取微信回调请求");
|
?? throw new BusinessException("无法读取微信回调请求");
|
||||||
return WeChatMpCallbackSupport.HandleMessageAsync(
|
return await WeChatMpCallbackSupport.HandleMessageAsync(
|
||||||
WeChatMpOptions,
|
options,
|
||||||
WeChatEventHandler,
|
WeChatEventHandler,
|
||||||
request,
|
request,
|
||||||
WeChatLogger,
|
WeChatLogger,
|
||||||
@ -101,11 +106,12 @@ public partial class AuthService
|
|||||||
[FromQuery(Name = "encrypt_type")] string? encryptType,
|
[FromQuery(Name = "encrypt_type")] string? encryptType,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
WeChatMpCallbackSupport.EnsureAppId(WeChatMpOptions, appId);
|
var options = await WeChatMpConfigProvider.GetAsync(cancellationToken);
|
||||||
|
WeChatMpCallbackSupport.EnsureAppId(options, appId);
|
||||||
var request = httpContextAccessor.HttpContext?.Request
|
var request = httpContextAccessor.HttpContext?.Request
|
||||||
?? throw new BusinessException("无法读取微信回调请求");
|
?? throw new BusinessException("无法读取微信回调请求");
|
||||||
return await WeChatMpCallbackSupport.HandleMessageAsync(
|
return await WeChatMpCallbackSupport.HandleMessageAsync(
|
||||||
WeChatMpOptions,
|
options,
|
||||||
WeChatEventHandler,
|
WeChatEventHandler,
|
||||||
request,
|
request,
|
||||||
WeChatLogger,
|
WeChatLogger,
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using Microsoft.Extensions.Caching.Memory;
|
using Microsoft.Extensions.Caching.Memory;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using SimpleAdmin.Api.WeChat.Options;
|
using SimpleAdmin.Api.WeChat.Options;
|
||||||
|
|
||||||
namespace SimpleAdmin.Api.WeChat.Services;
|
namespace SimpleAdmin.Api.WeChat.Services;
|
||||||
@ -30,7 +29,7 @@ public sealed record WeChatOAuthUserResult(
|
|||||||
internal sealed class WeChatMpApiClient(
|
internal sealed class WeChatMpApiClient(
|
||||||
IHttpClientFactory httpClientFactory,
|
IHttpClientFactory httpClientFactory,
|
||||||
IMemoryCache memoryCache,
|
IMemoryCache memoryCache,
|
||||||
IOptions<WeChatMpOptions> options,
|
IWeChatMpConfigProvider configProvider,
|
||||||
ILogger<WeChatMpApiClient> logger) : IWeChatMpApiClient
|
ILogger<WeChatMpApiClient> logger) : IWeChatMpApiClient
|
||||||
{
|
{
|
||||||
private const string AccessTokenCacheKey = "oms:wechat:mp:access_token";
|
private const string AccessTokenCacheKey = "oms:wechat:mp:access_token";
|
||||||
@ -78,7 +77,7 @@ internal sealed class WeChatMpApiClient(
|
|||||||
|
|
||||||
public async Task<WeChatOAuthTokenResult?> GetOAuthAccessTokenAsync(string code, CancellationToken cancellationToken = default)
|
public async Task<WeChatOAuthTokenResult?> GetOAuthAccessTokenAsync(string code, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var mp = options.Value;
|
var mp = await configProvider.GetAsync(cancellationToken);
|
||||||
var client = httpClientFactory.CreateClient(nameof(WeChatMpApiClient));
|
var client = httpClientFactory.CreateClient(nameof(WeChatMpApiClient));
|
||||||
var url =
|
var url =
|
||||||
$"https://api.weixin.qq.com/sns/oauth2/access_token?appid={Uri.EscapeDataString(mp.AppId)}&secret={Uri.EscapeDataString(mp.Secret)}&code={Uri.EscapeDataString(code)}&grant_type=authorization_code";
|
$"https://api.weixin.qq.com/sns/oauth2/access_token?appid={Uri.EscapeDataString(mp.AppId)}&secret={Uri.EscapeDataString(mp.Secret)}&code={Uri.EscapeDataString(code)}&grant_type=authorization_code";
|
||||||
@ -131,7 +130,7 @@ internal sealed class WeChatMpApiClient(
|
|||||||
return cached;
|
return cached;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mp = options.Value;
|
var mp = await configProvider.GetAsync(cancellationToken);
|
||||||
var client = httpClientFactory.CreateClient(nameof(WeChatMpApiClient));
|
var client = httpClientFactory.CreateClient(nameof(WeChatMpApiClient));
|
||||||
var url =
|
var url =
|
||||||
$"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={Uri.EscapeDataString(mp.AppId)}&secret={Uri.EscapeDataString(mp.Secret)}";
|
$"https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={Uri.EscapeDataString(mp.AppId)}&secret={Uri.EscapeDataString(mp.Secret)}";
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
using Fake.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using Oms.Domain.SystemAggregate.Consts;
|
||||||
|
using Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
using SimpleAdmin.Api.WeChat.Options;
|
||||||
|
|
||||||
|
namespace SimpleAdmin.Api.WeChat.Services;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 微信公众号运行时配置:优先读 t_system_other_config(wechat_mp),缺省回退 Consul/appsettings。
|
||||||
|
/// </summary>
|
||||||
|
public interface IWeChatMpConfigProvider
|
||||||
|
{
|
||||||
|
Task<WeChatMpOptions> GetAsync(CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class WeChatMpConfigProvider(
|
||||||
|
ISystemOtherConfigRepository repository,
|
||||||
|
IOptions<WeChatMpOptions> options) : IWeChatMpConfigProvider, IScopedDependency
|
||||||
|
{
|
||||||
|
public async Task<WeChatMpOptions> GetAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var fallback = options.Value;
|
||||||
|
var entity = await repository.GetByConfigCodeAsync(SystemOtherConfigCodes.WeChatMp, cancellationToken);
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new WeChatMpOptions
|
||||||
|
{
|
||||||
|
AppId = Coalesce(entity.AppKey, fallback.AppId),
|
||||||
|
Secret = Coalesce(entity.AppSecret, fallback.Secret),
|
||||||
|
Token = Coalesce(entity.Remark, fallback.Token),
|
||||||
|
OAuthRedirectBase = Coalesce(entity.BaseUrl, fallback.OAuthRedirectBase),
|
||||||
|
EncodingAesKey = fallback.EncodingAesKey
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string Coalesce(string? primary, string fallback) =>
|
||||||
|
string.IsNullOrWhiteSpace(primary) ? fallback : primary.Trim();
|
||||||
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using Fake.Application;
|
using Fake.Application;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using SimpleAdmin.Api.WeChat.Options;
|
using SimpleAdmin.Api.WeChat.Options;
|
||||||
using SimpleAdmin.Api.WeChat.Services;
|
using SimpleAdmin.Api.WeChat.Services;
|
||||||
|
|
||||||
@ -9,14 +8,14 @@ namespace SimpleAdmin.Api.WeChat;
|
|||||||
internal static class WeChatMpCallbackSupport
|
internal static class WeChatMpCallbackSupport
|
||||||
{
|
{
|
||||||
public static ContentResult Validate(
|
public static ContentResult Validate(
|
||||||
IOptions<WeChatMpOptions> options,
|
WeChatMpOptions options,
|
||||||
string? signature,
|
string? signature,
|
||||||
string? timestamp,
|
string? timestamp,
|
||||||
string? nonce,
|
string? nonce,
|
||||||
string? echostr)
|
string? echostr)
|
||||||
{
|
{
|
||||||
// 微信服务器要求 HTTP 200 + 纯文本 echostr,不可抛 BusinessException(会变成 JSON 非 200)
|
// 微信服务器要求 HTTP 200 + 纯文本 echostr,不可抛 BusinessException(会变成 JSON 非 200)
|
||||||
if (string.IsNullOrWhiteSpace(options.Value.Token))
|
if (string.IsNullOrWhiteSpace(options.Token))
|
||||||
{
|
{
|
||||||
return PlainText("error");
|
return PlainText("error");
|
||||||
}
|
}
|
||||||
@ -29,12 +28,12 @@ internal static class WeChatMpCallbackSupport
|
|||||||
return PlainText("error");
|
return PlainText("error");
|
||||||
}
|
}
|
||||||
|
|
||||||
var valid = WeChatSignatureHelper.Validate(options.Value.Token, timestamp, nonce, signature);
|
var valid = WeChatSignatureHelper.Validate(options.Token, timestamp, nonce, signature);
|
||||||
return PlainText(valid ? echostr : "error");
|
return PlainText(valid ? echostr : "error");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static async Task<ContentResult> HandleMessageAsync(
|
public static async Task<ContentResult> HandleMessageAsync(
|
||||||
IOptions<WeChatMpOptions> options,
|
WeChatMpOptions options,
|
||||||
IWeChatEventHandler eventHandler,
|
IWeChatEventHandler eventHandler,
|
||||||
HttpRequest request,
|
HttpRequest request,
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
@ -44,7 +43,7 @@ internal static class WeChatMpCallbackSupport
|
|||||||
string? encryptType,
|
string? encryptType,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (!WeChatSignatureHelper.Validate(options.Value.Token, timestamp, nonce, signature))
|
if (!WeChatSignatureHelper.Validate(options.Token, timestamp, nonce, signature))
|
||||||
{
|
{
|
||||||
logger.LogWarning("微信消息验签失败");
|
logger.LogWarning("微信消息验签失败");
|
||||||
return PlainText("error");
|
return PlainText("error");
|
||||||
@ -76,9 +75,9 @@ internal static class WeChatMpCallbackSupport
|
|||||||
return Xml(reply ?? string.Empty);
|
return Xml(reply ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void EnsureAppId(IOptions<WeChatMpOptions> options, string appId)
|
public static void EnsureAppId(WeChatMpOptions options, string appId)
|
||||||
{
|
{
|
||||||
var configured = options.Value.AppId;
|
var configured = options.AppId;
|
||||||
if (string.IsNullOrWhiteSpace(configured))
|
if (string.IsNullOrWhiteSpace(configured))
|
||||||
{
|
{
|
||||||
throw new BusinessException("未配置微信公众号 AppId");
|
throw new BusinessException("未配置微信公众号 AppId");
|
||||||
|
|||||||
@ -23,7 +23,7 @@ public static class WeChatServiceExtensions
|
|||||||
services.AddDistributedMemoryCache();
|
services.AddDistributedMemoryCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
services.AddSingleton<IWeChatMpApiClient, WeChatMpApiClient>();
|
services.AddScoped<IWeChatMpApiClient, WeChatMpApiClient>();
|
||||||
services.AddSingleton<IWeChatLoginSessionStore, WeChatLoginSessionStore>();
|
services.AddSingleton<IWeChatLoginSessionStore, WeChatLoginSessionStore>();
|
||||||
services.AddTransient<IWeChatEventHandler, WeChatEventHandler>();
|
services.AddTransient<IWeChatEventHandler, WeChatEventHandler>();
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user