diff --git a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs index d5d74bf43..3b20014cd 100644 --- a/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs +++ b/aspnet-core/modules/account/LINGYUN.Abp.Account.Application/LINGYUN/Abp/Account/AccountAppService.cs @@ -30,7 +30,7 @@ namespace LINGYUN.Abp.Account protected IIdentityUserRepository UserRepository { get; } protected IUserSecurityCodeSender SecurityCodeSender { get; } protected IWeChatOpenIdFinder WeChatOpenIdFinder { get; } - protected AbpWeChatMiniProgramOptions MiniProgramOptions { get; } + protected AbpWeChatMiniProgramOptionsFactory MiniProgramOptionsFactory { get; } protected IDistributedCache SecurityTokenCache { get; } @@ -42,7 +42,7 @@ namespace LINGYUN.Abp.Account IIdentityUserRepository userRepository, IUserSecurityCodeSender securityCodeSender, IDistributedCache securityTokenCache, - IOptions miniProgramOptions) + AbpWeChatMiniProgramOptionsFactory miniProgramOptionsFactory) { TotpService = totpService; UserStore = userStore; @@ -51,7 +51,7 @@ namespace LINGYUN.Abp.Account WeChatOpenIdFinder = weChatOpenIdFinder; SecurityCodeSender = securityCodeSender; SecurityTokenCache = securityTokenCache; - MiniProgramOptions = miniProgramOptions.Value; + MiniProgramOptionsFactory = miniProgramOptionsFactory; LocalizationResource = typeof(AccountResource); } @@ -61,7 +61,9 @@ namespace LINGYUN.Abp.Account ThowIfInvalidEmailAddress(input.EmailAddress); await CheckSelfRegistrationAsync(); - var wehchatOpenId = await WeChatOpenIdFinder.FindAsync(input.Code, MiniProgramOptions.AppId, MiniProgramOptions.AppSecret); + var options = await MiniProgramOptionsFactory.CreateAsync(); + + var wehchatOpenId = await WeChatOpenIdFinder.FindAsync(input.Code, options.AppId, options.AppSecret); var user = await UserManager.FindByLoginAsync(AbpWeChatMiniProgramConsts.ProviderKey, wehchatOpenId.OpenId); if (user != null) diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs index 221483b96..3ecd61730 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs +++ b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/MiniProgram/WeChatMiniProgramGrantValidator.cs @@ -22,7 +22,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram public override string AuthenticationMethod => AbpWeChatMiniProgramConsts.AuthenticationMethod; - protected AbpWeChatMiniProgramOptions Options { get; } + protected AbpWeChatMiniProgramOptionsFactory MiniProgramOptionsFactory { get; } public WeChatMiniProgramGrantValidator( IEventService eventService, @@ -31,15 +31,17 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.MiniProgram IIdentityUserRepository userRepository, IStringLocalizer identityLocalizer, IStringLocalizer identityServerLocalizer, - IOptions options) + AbpWeChatMiniProgramOptionsFactory miniProgramOptionsFactory) : base(eventService, weChatOpenIdFinder, userManager, userRepository, identityLocalizer, identityServerLocalizer) { - Options = options.Value; + MiniProgramOptionsFactory = miniProgramOptionsFactory; } protected override async Task FindOpenIdAsync(string code) { - return await WeChatOpenIdFinder.FindAsync(code, Options.AppId, Options.AppSecret); + var options = await MiniProgramOptionsFactory.CreateAsync(); + + return await WeChatOpenIdFinder.FindAsync(code, options.AppId, options.AppSecret); } } } diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs index 548b8b8d1..dc43b6e78 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs +++ b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialGrantValidator.cs @@ -3,7 +3,6 @@ using LINGYUN.Abp.WeChat.Official; using LINGYUN.Abp.WeChat.OpenId; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Localization; -using Microsoft.Extensions.Options; using System.Threading.Tasks; using Volo.Abp.Identity; using Volo.Abp.IdentityServer.Localization; @@ -22,7 +21,7 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official public override string AuthenticationMethod => AbpWeChatOfficialConsts.AuthenticationMethod; - protected AbpWeChatOfficialOptions Options { get; } + protected AbpWeChatOfficialOptionsFactory WeChatOfficialOptionsFactory { get; } public WeChatOfficialGrantValidator( IEventService eventService, @@ -31,15 +30,17 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official IIdentityUserRepository userRepository, IStringLocalizer identityLocalizer, IStringLocalizer identityServerLocalizer, - IOptions options) + AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory) : base(eventService, weChatOpenIdFinder, userManager, userRepository, identityLocalizer, identityServerLocalizer) { - Options = options.Value; + WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory; } protected override async Task FindOpenIdAsync(string code) { - return await WeChatOpenIdFinder.FindAsync(code, Options.AppId, Options.AppSecret); + var options = await WeChatOfficialOptionsFactory.CreateAsync(); + + return await WeChatOpenIdFinder.FindAsync(code, options.AppId, options.AppSecret); } } } diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialSignatureMiddleware.cs b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialSignatureMiddleware.cs index 7aa651177..b326504ca 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialSignatureMiddleware.cs +++ b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/LINGYUN/Abp/IdentityServer/WeChat/Official/WeChatOfficialSignatureMiddleware.cs @@ -1,6 +1,5 @@ using LINGYUN.Abp.WeChat.Official; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.Options; using System; using System.Collections; using System.Threading.Tasks; @@ -11,26 +10,29 @@ namespace LINGYUN.Abp.IdentityServer.WeChat.Official { public class WeChatOfficialSignatureMiddleware : IMiddleware, ITransientDependency { - protected AbpWeChatOfficialOptions Options { get; } - public WeChatOfficialSignatureMiddleware(IOptions options) + protected AbpWeChatOfficialOptionsFactory WeChatOfficialOptionsFactory { get; } + public WeChatOfficialSignatureMiddleware( + AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory) { - Options = options.Value; + WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory; } public async Task InvokeAsync(HttpContext context, RequestDelegate next) { if (context.Request.Path.HasValue) { + var options = await WeChatOfficialOptionsFactory.CreateAsync(); + var requestPath = context.Request.Path.Value; // 访问地址是否与定义的地址匹配 - if (requestPath.Equals(Options.Url)) + if (requestPath.Equals(options.Url)) { var timestamp = context.Request.Query["timestamp"]; var nonce = context.Request.Query["nonce"]; var signature = context.Request.Query["signature"]; var echostr = context.Request.Query["echostr"]; // 验证消息合法性 - var check = CheckWeChatSignature(Options.Token, timestamp, nonce, signature); + var check = CheckWeChatSignature(options.Token, timestamp, nonce, signature); if (check) { // 验证通过需要把微信服务器传递的字符原封不动传回 diff --git a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs index ffbc716f2..607ffcc8a 100644 --- a/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs +++ b/aspnet-core/modules/identityServer/LINGYUN.Abp.IdentityServer.WeChat/Microsoft/AspNetCore/Authentication/WeChat/Official/WeChatOfficialOAuthHandler.cs @@ -26,26 +26,29 @@ namespace Microsoft.AspNetCore.Authentication.WeChat.Official public class WeChatOfficialOAuthHandler : OAuthHandler { protected IDistributedCache Cache { get; } - protected AbpWeChatOfficialOptions WeChatOfficialOptions { get; } + protected AbpWeChatOfficialOptionsFactory WeChatOfficialOptionsFactory { get; } public WeChatOfficialOAuthHandler( IDistributedCache cache, - IOptionsMonitor options, - IOptions weChatOfficialOptions, + IOptionsMonitor options, + AbpWeChatOfficialOptionsFactory weChatOfficialOptionsFactory, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { Cache = cache; - WeChatOfficialOptions = weChatOfficialOptions.Value; + WeChatOfficialOptionsFactory = weChatOfficialOptionsFactory; } - protected override Task InitializeHandlerAsync() + protected override async Task InitializeHandlerAsync() { + var weChatOfficialOptions = await WeChatOfficialOptionsFactory.CreateAsync(); + // 用配置项重写 - Options.ClientId = WeChatOfficialOptions.AppId; - Options.ClientSecret = WeChatOfficialOptions.AppSecret; - return base.InitializeHandlerAsync(); + Options.ClientId = weChatOfficialOptions.AppId; + Options.ClientSecret = weChatOfficialOptions.AppSecret; + + await base.InitializeHandlerAsync(); } protected override async Task CreateTicketAsync(ClaimsIdentity identity, AuthenticationProperties properties, OAuthTokenResponse tokens) diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramModule.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramModule.cs index 16c66302a..00db3f7d1 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramModule.cs +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramModule.cs @@ -1,7 +1,5 @@ using LINGYUN.Abp.WeChat.Localization; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; using System; using Volo.Abp.Localization; using Volo.Abp.Modularity; @@ -34,13 +32,7 @@ namespace LINGYUN.Abp.WeChat.MiniProgram options.BaseAddress = new Uri("https://api.weixin.qq.com"); }); - AddAbpWeChatMiniProgramOptionsFactory(context.Services); - } - - private static void AddAbpWeChatMiniProgramOptionsFactory(IServiceCollection services) - { - services.Replace(ServiceDescriptor.Transient, AbpWeChatMiniProgramOptionsFactory>()); - services.Replace(ServiceDescriptor.Scoped, OptionsManager>()); + context.Services.AddAbpDynamicOptions(); } } } diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramOptionsFactory.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramOptionsFactory.cs index 52cbc0b89..cd8b603c9 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramOptionsFactory.cs +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramOptionsFactory.cs @@ -1,50 +1,24 @@ -using LINGYUN.Abp.WeChat.MiniProgram.Settings; -using Microsoft.Extensions.Options; -using System.Collections.Generic; +using Microsoft.Extensions.Options; using System.Threading.Tasks; -using Volo.Abp.Options; -using Volo.Abp.Settings; -using Volo.Abp.Threading; +using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.WeChat.MiniProgram { - public class AbpWeChatMiniProgramOptionsFactory : AbpOptionsFactory + public class AbpWeChatMiniProgramOptionsFactory : ITransientDependency { - protected ISettingProvider SettingProvider { get; } - public AbpWeChatMiniProgramOptionsFactory( - ISettingProvider settingProvider, - IEnumerable> setups, - IEnumerable> postConfigures) - : base(setups, postConfigures) - { - SettingProvider = settingProvider; - } - - public override AbpWeChatMiniProgramOptions Create(string name) - { - var options = base.Create(name); + protected IOptions Options { get; } - OverrideOptions(options); - - return options; - } - - protected virtual void OverrideOptions(AbpWeChatMiniProgramOptions options) + public AbpWeChatMiniProgramOptionsFactory( + IOptions options) { - AsyncHelper.RunSync(() => OverrideOptionsAsync(options)); + Options = options; } - protected virtual async Task OverrideOptionsAsync(AbpWeChatMiniProgramOptions options) + public virtual async Task CreateAsync() { - var appId = await SettingProvider.GetOrNullAsync(WeChatMiniProgramSettingNames.AppId); - var appSecret = await SettingProvider.GetOrNullAsync(WeChatMiniProgramSettingNames.AppSecret); - var token = await SettingProvider.GetOrNullAsync(WeChatMiniProgramSettingNames.Token); - var aesKey = await SettingProvider.GetOrNullAsync(WeChatMiniProgramSettingNames.EncodingAESKey); + await Options.SetAsync(); - options.AppId = appId ?? options.AppId; - options.AppSecret = appSecret ?? options.AppSecret; - options.Token = token ?? options.Token; - options.EncodingAESKey = aesKey ?? options.EncodingAESKey; + return Options.Value; } } } diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramOptionsManager.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramOptionsManager.cs new file mode 100644 index 000000000..1f6d26ce8 --- /dev/null +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/AbpWeChatMiniProgramOptionsManager.cs @@ -0,0 +1,34 @@ +using LINGYUN.Abp.WeChat.MiniProgram.Settings; +using Microsoft.Extensions.Options; +using System.Threading.Tasks; +using Volo.Abp.Options; +using Volo.Abp.Settings; + +namespace LINGYUN.Abp.WeChat.MiniProgram +{ + public class AbpWeChatMiniProgramOptionsManager : AbpDynamicOptionsManager + { + protected ISettingProvider SettingProvider { get; } + + public AbpWeChatMiniProgramOptionsManager( + ISettingProvider settingProvider, + IOptionsFactory factory) + : base(factory) + { + SettingProvider = settingProvider; + } + + protected override async Task OverrideOptionsAsync(string name, AbpWeChatMiniProgramOptions options) + { + var appId = await SettingProvider.GetOrNullAsync(WeChatMiniProgramSettingNames.AppId); + var appSecret = await SettingProvider.GetOrNullAsync(WeChatMiniProgramSettingNames.AppSecret); + var token = await SettingProvider.GetOrNullAsync(WeChatMiniProgramSettingNames.Token); + var aesKey = await SettingProvider.GetOrNullAsync(WeChatMiniProgramSettingNames.EncodingAESKey); + + options.AppId = appId ?? options.AppId; + options.AppSecret = appSecret ?? options.AppSecret; + options.Token = token ?? options.Token; + options.EncodingAESKey = aesKey ?? options.EncodingAESKey; + } + } +} diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/Messages/SubscribeMessager.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/Messages/SubscribeMessager.cs index d59adb134..8867c3e01 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/Messages/SubscribeMessager.cs +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/LINGYUN/Abp/WeChat/MiniProgram/Messages/SubscribeMessager.cs @@ -20,7 +20,7 @@ namespace LINGYUN.Abp.WeChat.MiniProgram.Messages public ILogger Logger { get; set; } protected IHttpClientFactory HttpClientFactory { get; } protected IJsonSerializer JsonSerializer { get; } - protected AbpWeChatMiniProgramOptions MiniProgramOptions { get; } + protected AbpWeChatMiniProgramOptionsFactory MiniProgramOptionsFactory { get; } protected IWeChatTokenProvider WeChatTokenProvider { get; } protected IUserWeChatOpenIdFinder UserWeChatOpenIdFinder { get; } public SubscribeMessager( @@ -28,13 +28,13 @@ namespace LINGYUN.Abp.WeChat.MiniProgram.Messages IHttpClientFactory httpClientFactory, IWeChatTokenProvider weChatTokenProvider, IUserWeChatOpenIdFinder userWeChatOpenIdFinder, - IOptions miniProgramOptions) + AbpWeChatMiniProgramOptionsFactory miniProgramOptionsFactory) { JsonSerializer = jsonSerializer; HttpClientFactory = httpClientFactory; WeChatTokenProvider = weChatTokenProvider; UserWeChatOpenIdFinder = userWeChatOpenIdFinder; - MiniProgramOptions = miniProgramOptions.Value; + MiniProgramOptionsFactory = miniProgramOptionsFactory; Logger = NullLogger.Instance; } @@ -64,7 +64,9 @@ namespace LINGYUN.Abp.WeChat.MiniProgram.Messages public virtual async Task SendAsync(SubscribeMessage message, CancellationToken cancellationToken = default) { - var weChatToken = await WeChatTokenProvider.GetTokenAsync(MiniProgramOptions.AppId, MiniProgramOptions.AppSecret, cancellationToken); + var options = await MiniProgramOptionsFactory.CreateAsync(); + + var weChatToken = await WeChatTokenProvider.GetTokenAsync(options.AppId, options.AppSecret, cancellationToken); var requestParamters = new Dictionary { { "access_token", weChatToken.AccessToken } diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/README.md b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/README.md index 9a399fde7..56dc35960 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/README.md +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.MiniProgram/README.md @@ -7,7 +7,8 @@ #### 注意 - +在动态配置中有一个已知的问题: https://github.com/abpframework/abp/issues/6318 +因此必须要重建一个动态变更 AbpWeChatMiniProgramOptions 的方法,请使用AbpWeChatMiniProgramOptionsFactory.CreateAsync() ## 配置使用 diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs index 70e7804c4..4ce2cfbab 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialModule.cs @@ -1,7 +1,5 @@ using LINGYUN.Abp.WeChat.Localization; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; using Volo.Abp.Localization; using Volo.Abp.Modularity; using Volo.Abp.VirtualFileSystem; @@ -26,13 +24,7 @@ namespace LINGYUN.Abp.WeChat.Official .AddVirtualJson("/LINGYUN/Abp/WeChat/Official/Localization/Resources"); }); - AddAbpWeChatOfficialOptionsFactory(context.Services); - } - - private static void AddAbpWeChatOfficialOptionsFactory(IServiceCollection services) - { - services.Replace(ServiceDescriptor.Transient, AbpWeChatOfficialOptionsFactory>()); - services.Replace(ServiceDescriptor.Scoped, OptionsManager>()); + context.Services.AddAbpDynamicOptions(); } } } diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsFactory.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsFactory.cs index ecc91073a..e945ac9d7 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsFactory.cs +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsFactory.cs @@ -1,52 +1,24 @@ -using LINGYUN.Abp.WeChat.Official.Settings; -using Microsoft.Extensions.Options; -using System.Collections.Generic; +using Microsoft.Extensions.Options; using System.Threading.Tasks; -using Volo.Abp.Options; -using Volo.Abp.Settings; -using Volo.Abp.Threading; +using Volo.Abp.DependencyInjection; namespace LINGYUN.Abp.WeChat.Official { - public class AbpWeChatOfficialOptionsFactory : AbpOptionsFactory + public class AbpWeChatOfficialOptionsFactory : ITransientDependency { - protected ISettingProvider SettingProvider { get; } - public AbpWeChatOfficialOptionsFactory( - ISettingProvider settingProvider, - IEnumerable> setups, - IEnumerable> postConfigures) - : base(setups, postConfigures) - { - SettingProvider = settingProvider; - } - - public override AbpWeChatOfficialOptions Create(string name) - { - var options = base.Create(name); + protected IOptions Options { get; } - OverrideOptions(options); - - return options; - } - - protected virtual void OverrideOptions(AbpWeChatOfficialOptions options) + public AbpWeChatOfficialOptionsFactory( + IOptions options) { - AsyncHelper.RunSync(() => OverrideOptionsAsync(options)); + Options = options; } - protected virtual async Task OverrideOptionsAsync(AbpWeChatOfficialOptions options) + public virtual async Task CreateAsync() { - var appId = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.AppId); - var appSecret = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.AppSecret); - var url = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.Url); - var token = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.Token); - var aesKey = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.EncodingAESKey); + await Options.SetAsync(); - options.AppId = appId ?? options.AppId; - options.AppSecret = appSecret ?? options.AppSecret; - options.Url = url ?? options.Url; - options.Token = token ?? options.Token; - options.EncodingAESKey = aesKey ?? options.EncodingAESKey; + return Options.Value; } } } diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsManager.cs b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsManager.cs new file mode 100644 index 000000000..5dd42e928 --- /dev/null +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/LINGYUN/Abp/WeChat/Official/AbpWeChatOfficialOptionsManager.cs @@ -0,0 +1,35 @@ +using LINGYUN.Abp.WeChat.Official.Settings; +using Microsoft.Extensions.Options; +using System.Threading.Tasks; +using Volo.Abp.Options; +using Volo.Abp.Settings; + +namespace LINGYUN.Abp.WeChat.Official +{ + public class AbpWeChatOfficialOptionsManager : AbpDynamicOptionsManager + { + protected ISettingProvider SettingProvider { get; } + public AbpWeChatOfficialOptionsManager( + ISettingProvider settingProvider, + IOptionsFactory factory) + : base(factory) + { + SettingProvider = settingProvider; + } + + protected override async Task OverrideOptionsAsync(string name, AbpWeChatOfficialOptions options) + { + var appId = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.AppId); + var appSecret = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.AppSecret); + var url = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.Url); + var token = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.Token); + var aesKey = await SettingProvider.GetOrNullAsync(WeChatOfficialSettingNames.EncodingAESKey); + + options.AppId = appId ?? options.AppId; + options.AppSecret = appSecret ?? options.AppSecret; + options.Url = url ?? options.Url; + options.Token = token ?? options.Token; + options.EncodingAESKey = aesKey ?? options.EncodingAESKey; + } + } +} diff --git a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/README.md b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/README.md index bf1c03302..c52069eab 100644 --- a/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/README.md +++ b/aspnet-core/modules/wechat/LINGYUN.Abp.WeChat.Official/README.md @@ -7,7 +7,8 @@ #### 注意 - +在动态配置中有一个已知的问题: https://github.com/abpframework/abp/issues/6318 +因此必须要重建一个动态变更 AbpWeChatOfficialOptions 的方法,请使用AbpWeChatOfficialOptionsFactory.CreateAsync() ## 配置使用 diff --git a/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs b/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs index 0f3ef67f0..d52f9d9fb 100644 --- a/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs +++ b/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; +using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -154,6 +155,16 @@ namespace AuthServer.Host options.InstanceName = configuration["Redis:InstanceName"]; }); + // 增加配置文件定义,在新建租户时需要 + Configure(options => + { + var identityConfiguration = configuration.GetSection("Identity"); + if (identityConfiguration.Exists()) + { + identityConfiguration.Bind(options); + } + }); + Configure(options => { options.FileSets.AddEmbedded("AuthServer"); diff --git a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs new file mode 100644 index 000000000..32d60ac56 --- /dev/null +++ b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/EventBus/Handlers/TenantSynchronizer.cs @@ -0,0 +1,525 @@ +using LINGYUN.Abp.MultiTenancy; +using LINGYUN.Platform.Datas; +using LINGYUN.Platform.Layouts; +using LINGYUN.Platform.Menus; +using LINGYUN.Platform.Routes; +using LINGYUN.Platform.Utils; +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.Data; +using Volo.Abp.DependencyInjection; +using Volo.Abp.EventBus.Distributed; +using Volo.Abp.Guids; +using Volo.Abp.MultiTenancy; + +namespace LINGYUN.Platform.EventBus.Handlers +{ + public class TenantSynchronizer : + IDistributedEventHandler, + ITransientDependency + { + protected ICurrentTenant CurrentTenant { get; } + protected IGuidGenerator GuidGenerator { get; } + protected IRouteDataSeeder RouteDataSeeder { get; } + protected IDataDictionaryDataSeeder DataDictionaryDataSeeder { get; } + protected IMenuRepository MenuRepository { get; } + protected ILayoutRepository LayoutRepository { get; } + + public TenantSynchronizer( + ICurrentTenant currentTenant, + IRouteDataSeeder routeDataSeeder, + IMenuRepository menuRepository, + ILayoutRepository layoutRepository, + IGuidGenerator guidGenerator, + IDataDictionaryDataSeeder dataDictionaryDataSeeder) + { + CurrentTenant = currentTenant; + GuidGenerator = guidGenerator; + RouteDataSeeder = routeDataSeeder; + MenuRepository = menuRepository; + LayoutRepository = layoutRepository; + DataDictionaryDataSeeder = dataDictionaryDataSeeder; + } + + /// + /// 租户创建之后需要预置租户平台数据 + /// + /// + /// + public virtual async Task HandleEventAsync(CreateEventData eventData) + { + using (CurrentTenant.Change(eventData.Id)) + { + var data = await SeedDefaultDataDictionaryAsync(eventData.Id); + // 预置 + var layout = await SeedDefaultLayoutAsync(data); + // 首页 + await SeedHomeMenuAsync(layout, data); + // 管理菜单预置菜单数据 + await SeedAdminMenuAsync(layout, data); + // 审计日志菜单数据 + await SeedAuditingMenuAsync(layout, data); + // 布局容器预置菜单数据 + await SeedContainerMenuAsync(layout, data); + } + } + + /// + /// 租户删除之后删除租户平台数据 + /// TODO: 不应删除用户数据 + /// + /// + /// + //public virtual async Task HandleEventAsync(DeleteEventData eventData) + //{ + // //await MenuRepository.GetAllAsync(); + //} + + private async Task SeedDefaultDataDictionaryAsync(Guid? tenantId) + { + var data = await DataDictionaryDataSeeder + .SeedAsync( + "Layout", + CodeNumberGenerator.CreateCode(1), + "Vue Admin Layout Meta Dictionary", + "Vue Admin Layout Meta Dictionary", + null, + tenantId); + + data.AddItem( + GuidGenerator, + "roles", // TODO: 是否需要把这一项写入到预置数据? + "roles", + "", + Datas.ValueType.Array, + "will control the page roles (allow setting multiple roles)"); + data.AddItem( + GuidGenerator, + "title", + "title", + "component", + Datas.ValueType.String, + "the name showed in subMenu and breadcrumb (recommend set)"); + data.AddItem( + GuidGenerator, + "icon", + "icon", + "icon", + Datas.ValueType.String, + "the icon showed in the sidebar"); + data.AddItem( + GuidGenerator, + "hidden", + "hidden", + "false", + Datas.ValueType.Boolean, + "if true, this route will not show in the sidebar (default is false)"); + data.AddItem( + GuidGenerator, + "alwaysShow", + "alwaysShow", + "false", + Datas.ValueType.Boolean, + "if true, will always show the root menu (default is false)"); + data.AddItem( + GuidGenerator, + "breadcrumb", + "breadcrumb", + "true", + Datas.ValueType.Boolean, + "if false, the item will be hidden in breadcrumb (default is true)"); + data.AddItem( + GuidGenerator, + "noCache", + "noCache", + "false", + Datas.ValueType.Boolean, + "if true, the page will not be cached (default is false)"); + data.AddItem( + GuidGenerator, + "affix", + "affix", + "false", + Datas.ValueType.Boolean, + "if true, the tag will affix in the tags-view"); + data.AddItem( + GuidGenerator, + "activeMenu", + "activeMenu", + "", + Datas.ValueType.String, + "if set path, the sidebar will highlight the path you set"); + + return data; + } + + private async Task SeedDefaultLayoutAsync(Data data) + { + var layout = await RouteDataSeeder.SeedLayoutAsync( + "Layout", + "layout/index.vue", + "Vue Admin Layout", + data.Id, + PlatformType.WebMvvm, // 针对当前的vue管理页 + "", + "Vue Admin Layout", + data.TenantId + ); + + return layout; + } + + private async Task SeedHomeMenuAsync(Layout layout, Data data) + { + var adminMenu = await SeedMenuAsync( + layout, + data, + "home", + "/", + CodeNumberGenerator.CreateCode(1), + layout.Path, + "Home", + "/dashboard", + "Home", + null, + layout.TenantId, + new Dictionary() + { + { "title", "home" }, + { "icon", "home" }, + { "alwaysShow", true } + }, + // isPublic: true, + isPublic: false); // 首页应该是共有的页面 + + await SeedMenuAsync( + layout, + data, + "dashboard", + "dashboard", + CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(1)), + "views/dashboard/index.vue", + "Dashboard", + "", + "Dashboard", + adminMenu.Id, + layout.TenantId, + new Dictionary() + { + { "title", "dashboard" }, + { "icon", "dashboard" } + }, + isPublic: false); + } + + private async Task SeedAdminMenuAsync(Layout layout, Data data) + { + var adminMenu = await SeedMenuAsync( + layout, + data, + "admin", + "/admin", + CodeNumberGenerator.CreateCode(2), + layout.Path, + "Admin", + "", + "Admin", + null, + layout.TenantId, + new Dictionary() + { + { "title", "admin" }, + { "icon", "admin" }, + { "alwaysShow", true } + }, + new string[] { "admin" }); + + await SeedMenuAsync( + layout, + data, + "roles", + "roles", + CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(1)), + "views/admin/roles/index.vue", + "Manage Roles", + "", + "Manage Roles", + adminMenu.Id, + layout.TenantId, + new Dictionary() + { + { "title", "roles" }, + { "icon", "role" }, + { "roles", new string[] { "AbpIdentity.Roles" } } + }, + new string[] { "admin" }); + + await SeedMenuAsync( + layout, + data, + "users", + "users", + CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(2)), + "views/admin/users/index.vue", + "Manage Users", + "", + "Manage Users", + adminMenu.Id, + layout.TenantId, + new Dictionary() + { + { "title", "users" }, + { "icon", "users" }, + { "roles", new string[] { "AbpIdentity.Users" } } + }, + new string[] { "admin" }); + + await SeedMenuAsync( + layout, + data, + "organization-unit", + "organization-unit", + CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(3)), + "views/admin/organization-unit/index.vue", + "Manage Organization Units", + "", + "Manage Organization Units", + adminMenu.Id, + layout.TenantId, + new Dictionary() + { + { "title", "organization-unit" }, + { "icon", "organization-unit" }, + { "roles", new string[] { "AbpIdentity.OrganizationUnits" } } + }, + new string[] { "admin" }); + + await SeedMenuAsync( + layout, + data, + "data-dictionary", + "data-dictionary", + CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(5)), + "views/admin/data-dictionary/index.vue", + "Manage Data Dictionarys", + "", + "Manage Data Dictionarys", + adminMenu.Id, + layout.TenantId, + new Dictionary() + { + { "title", "data-dictionary" }, + { "icon", "data-dictionary" }, + { "roles", new string[] { "Platform.DataDictionary" } } + }, + new string[] { "admin" }); + + await SeedMenuAsync( + layout, + data, + "settings", + "settings", + CodeNumberGenerator.AppendCode(adminMenu.Code, CodeNumberGenerator.CreateCode(6)), + "views/admin/settings/index.vue", + "Manage Settings", + "", + "Manage Settings", + adminMenu.Id, + layout.TenantId, + new Dictionary() + { + { "title", "settings" }, + { "icon", "settings" }, + { "roles", new string[] { "AbpSettingManagement.Settings" } } + }, + new string[] { "admin" }); + } + + private async Task SeedAuditingMenuAsync(Layout layout, Data data) + { + var auditingMenu = await SeedMenuAsync( + layout, + data, + "auditing", + "/auditing", + CodeNumberGenerator.CreateCode(5), + layout.Path, + "Auditing", + "", + "Auditing", + null, + layout.TenantId, + new Dictionary() + { + { "title", "auditing" }, + { "icon", "auditing" }, + { "alwaysShow", true }, + { "roles", new string[]{ "AbpAuditing.AuditLog", "AbpAuditing.SecurityLog" } } + }, + new string[] { "admin" }); + + await SeedMenuAsync( + layout, + data, + "audit-log", + "audit-log", + CodeNumberGenerator.AppendCode(auditingMenu.Code, CodeNumberGenerator.CreateCode(1)), + "views/admin/auditing/audit-log/index.vue", + "Manage AuditLog", + "", + "Manage AuditLog", + auditingMenu.Id, + layout.TenantId, + new Dictionary() + { + { "title", "audit-log" }, + { "icon", "audit-log" }, + { "roles", new string[]{ "AbpAuditing.AuditLog" } } + }, + new string[] { "admin" }); + await SeedMenuAsync( + layout, + data, + "security-log", + "security-log", + CodeNumberGenerator.AppendCode(auditingMenu.Code, CodeNumberGenerator.CreateCode(2)), + "views/admin/auditing/security-log/index.vue", + "Manage SecurityLog", + "", + "Manage SecurityLog", + auditingMenu.Id, + layout.TenantId, + new Dictionary() + { + { "title", "security-log" }, + { "icon", "security-log" }, + { "roles", new string[]{ "AbpAuditing.SecurityLog" } } + }, + new string[] { "admin" }); + } + + private async Task SeedContainerMenuAsync(Layout layout, Data data) + { + var containerRoot = await SeedMenuAsync( + layout, + data, + "container", + "/container", + CodeNumberGenerator.CreateCode(6), + layout.Path, + "Container", + "", + "Manage Container", + null, + layout.TenantId, + new Dictionary() + { + { "title", "container" }, + { "icon", "container" }, + { "alwaysShow", true } + }, + new string[] { "admin" }); + + await SeedMenuAsync( + layout, + data, + "layouts", + "layouts", + CodeNumberGenerator.AppendCode(containerRoot.Code, CodeNumberGenerator.CreateCode(1)), + "views/container/layouts/index.vue", + "Manage Layouts", + "", + "Manage Layouts", + containerRoot.Id, + containerRoot.TenantId, + new Dictionary() + { + { "title", "layouts" }, + { "icon", "layout" } + }, + new string[] { "admin" }); + + await SeedMenuAsync( + layout, + data, + "menus", + "menus", + CodeNumberGenerator.AppendCode(containerRoot.Code, CodeNumberGenerator.CreateCode(2)), + "views/container/menus/index.vue", + "Manage Menus", + "", + "Manage Menus", + containerRoot.Id, + containerRoot.TenantId, + new Dictionary() + { + { "title", "menus" }, + { "icon", "menu" } + }, + new string[] { "admin" }); + } + + private async Task SeedMenuAsync( + Layout layout, + Data data, + string name, + string path, + string code, + string component, + string displayName, + string redirect = "", + string description = "", + Guid? parentId = null, + Guid? tenantId = null, + Dictionary meta = null, + string[] roles = null, + Guid[] users = null, + bool isPublic = false + ) + { + var menu = await RouteDataSeeder.SeedMenuAsync( + layout, + name, + path, + code, + component, + displayName, + redirect, + description, + parentId, + tenantId, + isPublic + ); + foreach (var item in data.Items) + { + menu.SetProperty(item.Name, item.DefaultValue); + } + if (meta != null) + { + foreach (var item in meta) + { + menu.SetProperty(item.Key, item.Value); + } + } + + if (roles != null) + { + foreach (var role in roles) + { + await RouteDataSeeder.SeedRoleMenuAsync(role, menu, tenantId); + } + } + + if (users != null) + { + foreach (var user in users) + { + await RouteDataSeeder.SeedUserMenuAsync(user, menu, tenantId); + } + } + + return menu; + } + } +}