diff --git a/aspnet-core/configuration/messages/LINGYUN.Abp.MessageService.HttpApi.Host/appsettings.Development.json b/aspnet-core/configuration/messages/LINGYUN.Abp.MessageService.HttpApi.Host/appsettings.Development.json index 97b761933..2501d64b5 100644 --- a/aspnet-core/configuration/messages/LINGYUN.Abp.MessageService.HttpApi.Host/appsettings.Development.json +++ b/aspnet-core/configuration/messages/LINGYUN.Abp.MessageService.HttpApi.Host/appsettings.Development.json @@ -1,4 +1,7 @@ { + "App": { + "CorsOrigins": "http://localhost:9527,http://127.0.0.1:30000" + }, "ConnectionStrings": { "Default": "Server=127.0.0.1;Database=Messages;User Id=root;Password=123456", "MessageService": "Server=127.0.0.1;Database=Messages;User Id=root;Password=123456", diff --git a/aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs b/aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs index 82d8c8412..d5ee7dae9 100644 --- a/aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs +++ b/aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPEventBusModule.cs @@ -4,6 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Volo.Abp; using Volo.Abp.BackgroundWorkers; using Volo.Abp.EventBus; +using Volo.Abp.ExceptionHandling; using Volo.Abp.Modularity; namespace LINGYUN.Abp.EventBus.CAP @@ -27,6 +28,18 @@ namespace LINGYUN.Abp.EventBus.CAP { configuration.GetSection("CAP:EventBus").Bind(options); context.Services.ExecutePreConfiguredActions(options); + //if (options.FailedThresholdCallback == null) + //{ + // options.FailedThresholdCallback = async (failed) => + // { + // var exceptionNotifier = failed.ServiceProvider.GetService(); + // if (exceptionNotifier != null) + // { + // // TODO: 作为异常处理? + // await exceptionNotifier.NotifyAsync(new AbpCAPExecutionFailedException(failed.MessageType, failed.Message)); + // } + // }; + //} }); } diff --git a/aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPExecutionFailedException.cs b/aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPExecutionFailedException.cs new file mode 100644 index 000000000..f8931ceda --- /dev/null +++ b/aspnet-core/modules/common/LINGYUN.Abp.EventBus.CAP/LINGYUN/Abp/EventBus/CAP/AbpCAPExecutionFailedException.cs @@ -0,0 +1,56 @@ +using DotNetCore.CAP.Messages; +using System; +using Volo.Abp; + +namespace LINGYUN.Abp.EventBus.CAP +{ + /// + /// AbpECAPExecutionFailedException + /// + public class AbpCAPExecutionFailedException : AbpException + { + /// + /// MessageType + /// + public MessageType MessageType { get; set; } + /// + /// Message + /// + public Message Origin { get; set; } + /// + /// constructor + /// + /// + /// + public AbpCAPExecutionFailedException(MessageType messageType, Message prigin) + { + MessageType = messageType; + Origin = prigin; + } + + /// + /// constructor + /// + /// + /// + /// + public AbpCAPExecutionFailedException(MessageType messageType, Message prigin, string message) : base(message) + { + MessageType = messageType; + Origin = prigin; + } + + /// + /// constructor + /// + /// + /// + /// + /// + public AbpCAPExecutionFailedException(MessageType messageType, Message prigin, string message, Exception innerException) : base(message, innerException) + { + MessageType = messageType; + Origin = prigin; + } + } +} diff --git a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs index 778e4c245..47f547acb 100644 --- a/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs +++ b/aspnet-core/modules/settings/LINGYUN.Abp.SettingManagement.Application/LINGYUN/Abp/SettingManagement/SettingAppService.cs @@ -92,10 +92,11 @@ namespace LINGYUN.Abp.SettingManagement continue; } - if (!setting.IsVisibleToClients) - { - continue; - } + // 既然是配置服务,那必须能管理所有配置才对 + //if (!setting.IsVisibleToClients) + //{ + // continue; + //} var settingValue = await SettingManager.GetOrNullAsync(setting.Name, providerName, providerKey); var settingInfo = new SettingDto diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantCreateEventHandler.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantCreateEventHandler.cs index c207ab918..bb832aa11 100644 --- a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantCreateEventHandler.cs +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantCreateEventHandler.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; using Volo.Abp.Caching; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; @@ -12,6 +14,7 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed { public class TenantCreateEventHandler : IDistributedEventHandler>, ITransientDependency { + private readonly ILogger _logger; private readonly ICurrentTenant _currentTenant; private readonly ITenantRepository _tenantRepository; private readonly IDistributedCache _cache; @@ -19,9 +22,11 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed public TenantCreateEventHandler( ICurrentTenant currentTenant, ITenantRepository tenantRepository, + ILogger logger, IDistributedCache cache) { _cache = cache; + _logger = logger; _currentTenant = currentTenant; _tenantRepository = tenantRepository; } @@ -29,22 +34,29 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed [UnitOfWork] public virtual async Task HandleEventAsync(EntityCreatedEto eventData) { - using (_currentTenant.Change(null)) + try { - var tenant = await _tenantRepository.FindAsync(eventData.Entity.Id, true); - if (tenant == null) + using (_currentTenant.Change(null)) { - return; - } - var connectionStrings = new ConnectionStrings(); - foreach (var tenantConnectionString in tenant.ConnectionStrings) - { - connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value; - } - var cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings); + var tenant = await _tenantRepository.FindAsync(eventData.Entity.Id, true); + if (tenant == null) + { + return; + } + var connectionStrings = new ConnectionStrings(); + foreach (var tenantConnectionString in tenant.ConnectionStrings) + { + connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value; + } + var cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings); - var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(eventData.Entity.Id.ToString()); - await _cache.SetAsync(cacheKey, cacheItem); + var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(eventData.Entity.Id.ToString()); + await _cache.SetAsync(cacheKey, cacheItem); + } + } + catch (Exception ex) + { + _logger.LogException(ex); } } } diff --git a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantUpdateEventHandler.cs b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantUpdateEventHandler.cs index 4fe9dd599..86f0e7ddc 100644 --- a/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantUpdateEventHandler.cs +++ b/aspnet-core/modules/tenants/LINGYUN.Abp.MultiTenancy.DbFinder/LINGYUN/Abp/MultiTenancy/DbFinder/EventBus/Distributed/TenantUpdateEventHandler.cs @@ -1,4 +1,6 @@ -using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using System; +using System.Threading.Tasks; using Volo.Abp.Caching; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; @@ -12,6 +14,7 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed { public class TenantUpdateEventHandler : IDistributedEventHandler>, ITransientDependency { + private readonly ILogger _logger; private readonly ICurrentTenant _currentTenant; private readonly ITenantRepository _tenantRepository; private readonly IDistributedCache _cache; @@ -19,9 +22,11 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed public TenantUpdateEventHandler( ICurrentTenant currentTenant, ITenantRepository tenantRepository, + ILogger logger, IDistributedCache cache) { _cache = cache; + _logger = logger; _currentTenant = currentTenant; _tenantRepository = tenantRepository; } @@ -29,22 +34,29 @@ namespace LINGYUN.Abp.MultiTenancy.DbFinder.EventBus.Distributed [UnitOfWork] public virtual async Task HandleEventAsync(EntityUpdatedEto eventData) { - using (_currentTenant.Change(null)) + try { - var tenant = await _tenantRepository.FindAsync(eventData.Entity.Id, true); - if (tenant == null) + using (_currentTenant.Change(null)) { - return; - } - var connectionStrings = new ConnectionStrings(); - foreach (var tenantConnectionString in tenant.ConnectionStrings) - { - connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value; - } - var cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings); + var tenant = await _tenantRepository.FindAsync(eventData.Entity.Id, true); + if (tenant == null) + { + return; + } + var connectionStrings = new ConnectionStrings(); + foreach (var tenantConnectionString in tenant.ConnectionStrings) + { + connectionStrings[tenantConnectionString.Name] = tenantConnectionString.Value; + } + var cacheItem = new TenantConfigurationCacheItem(tenant.Id, tenant.Name, connectionStrings); - var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(eventData.Entity.Id.ToString()); - await _cache.SetAsync(cacheKey, cacheItem); + var cacheKey = TenantConfigurationCacheItem.CalculateCacheKey(eventData.Entity.Id.ToString()); + await _cache.SetAsync(cacheKey, cacheItem); + } + } + catch(Exception ex) + { + _logger.LogException(ex); } } } diff --git a/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs b/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs index 543961a9c..b28fc772c 100644 --- a/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs +++ b/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs @@ -144,7 +144,7 @@ namespace AuthServer.Host if (!hostingEnvironment.IsDevelopment()) { - var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]); + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); context.Services .AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "AuthServer-Protection-Keys"); @@ -183,7 +183,10 @@ namespace AuthServer.Host app.UseIdentityServer(); app.UseAuditing(); - SeedData(context); + if (context.GetEnvironment().IsDevelopment()) + { + SeedData(context); + } } private void SeedData(ApplicationInitializationContext context) diff --git a/aspnet-core/services/account/AuthServer.Host/AuthServer.Host.csproj b/aspnet-core/services/account/AuthServer.Host/AuthServer.Host.csproj index 8b53e7a06..63fa249bc 100644 --- a/aspnet-core/services/account/AuthServer.Host/AuthServer.Host.csproj +++ b/aspnet-core/services/account/AuthServer.Host/AuthServer.Host.csproj @@ -40,7 +40,5 @@ - - - + diff --git a/aspnet-core/services/account/AuthServer.Host/Dockerfile b/aspnet-core/services/account/AuthServer.Host/Dockerfile index 7ebef4107..f2b7f48a8 100644 --- a/aspnet-core/services/account/AuthServer.Host/Dockerfile +++ b/aspnet-core/services/account/AuthServer.Host/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/dotnet/core/runtime:3.1 AS runtime +FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS runtime LABEL maintainer="colin.in@foxmail.com" WORKDIR /app diff --git a/aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs b/aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs index 99abb0a3b..613c0558c 100644 --- a/aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs +++ b/aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs @@ -25,7 +25,6 @@ using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; using StackExchange.Redis; using System; -using System.Collections.Generic; using System.Text; using Volo.Abp; using Volo.Abp.Account; @@ -148,7 +147,14 @@ namespace LINGYUN.BackendAdmin Configure(options => { // 加入需要处理的异常类型 - options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); }); // 自定义需要发送邮件通知的异常类型 Configure(options => @@ -157,8 +163,6 @@ namespace LINGYUN.BackendAdmin options.SendStackTrace = true; // 未指定异常接收者的默认接收邮件 options.DefaultReceiveEmail = "colin.in@foxmail.com"; - // 指定某种异常发送到哪个邮件 - options.HandReceivedException("colin.in@foxmail.com"); }); @@ -208,6 +212,25 @@ namespace LINGYUN.BackendAdmin options.SwaggerDoc("v1", new OpenApiInfo { Title = "BackendAdmin API", Version = "v1" }); options.DocInclusionPredicate((docName, description) => true); options.CustomSchemaIds(type => type.FullName); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Scheme = "bearer", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT" + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } + }, + new string[] { } + } + }); }); // 支持本地化语言类型 @@ -245,7 +268,7 @@ namespace LINGYUN.BackendAdmin if (!hostingEnvironment.IsDevelopment()) { - var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]); + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); context.Services .AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "BackendAdmin-Protection-Keys"); @@ -283,7 +306,10 @@ namespace LINGYUN.BackendAdmin // 路由 app.UseConfiguredEndpoints(); - SeedData(context); + if (context.GetEnvironment().IsDevelopment()) + { + SeedData(context); + } } private void SeedData(ApplicationInitializationContext context) diff --git a/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs index 991272cb8..704648bf5 100644 --- a/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs +++ b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs @@ -128,6 +128,25 @@ namespace LINGYUN.ApiGateway options.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiGateway API", Version = "v1" }); options.DocInclusionPredicate((docName, description) => true); options.CustomSchemaIds(type => type.FullName); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Scheme = "bearer", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT" + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } + }, + new string[] { } + } + }); }); // 支持本地化语言类型 @@ -152,7 +171,7 @@ namespace LINGYUN.ApiGateway if (!hostingEnvironment.IsDevelopment()) { - var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]); + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); context.Services .AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "ApiGateway-Protection-Keys"); @@ -191,7 +210,10 @@ namespace LINGYUN.ApiGateway // 路由 app.UseConfiguredEndpoints(); - SeedData(context); + if (context.GetEnvironment().IsDevelopment()) + { + SeedData(context); + } } private void SeedData(ApplicationInitializationContext context) diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/AbpMessageServiceHttpApiHostModule.cs similarity index 73% rename from aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs rename to aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/AbpMessageServiceHttpApiHostModule.cs index 4b7729474..d12a51e50 100644 --- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/AbpMessageServiceHttpApiHostModule.cs +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/AbpMessageServiceHttpApiHostModule.cs @@ -8,6 +8,7 @@ using LINGYUN.Abp.ExceptionHandling; using LINGYUN.Abp.ExceptionHandling.Notifications; using LINGYUN.Abp.Hangfire.Storage.MySql; using LINGYUN.Abp.IM.SignalR; +using LINGYUN.Abp.MessageService.Authorization; using LINGYUN.Abp.MessageService.EntityFrameworkCore; using LINGYUN.Abp.MessageService.Localization; using LINGYUN.Abp.MessageService.MultiTenancy; @@ -15,15 +16,18 @@ using LINGYUN.Abp.MultiTenancy.DbFinder; using LINGYUN.Abp.Notifications.SignalR; using LINGYUN.Abp.Notifications.WeChat.WeApp; using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Cors; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Caching.StackExchangeRedis; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; using Microsoft.OpenApi.Models; using StackExchange.Redis; using System; +using System.Linq; using System.Text; using Volo.Abp; using Volo.Abp.AspNetCore.Authentication.JwtBearer; @@ -67,6 +71,8 @@ namespace LINGYUN.Abp.MessageService )] public class AbpMessageServiceHttpApiHostModule : AbpModule { + private const string DefaultCorsPolicyName = "Default"; + public override void PreConfigureServices(ServiceConfigurationContext context) { var configuration = context.Services.GetConfiguration(); @@ -104,7 +110,14 @@ namespace LINGYUN.Abp.MessageService Configure(options => { // 加入需要处理的异常类型 - // options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); }); Configure(options => @@ -161,7 +174,55 @@ namespace LINGYUN.Abp.MessageService options.SwaggerDoc("v1", new OpenApiInfo { Title = "MessageService API", Version = "v1" }); options.DocInclusionPredicate((docName, description) => true); options.CustomSchemaIds(type => type.FullName); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Scheme = "bearer", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT" + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } + }, + new string[] { } + } + }); + }); + + context.Services.AddCors(options => + { + options.AddPolicy(DefaultCorsPolicyName, builder => + { + builder + .WithOrigins( + configuration["App:CorsOrigins"] + .Split(",", StringSplitOptions.RemoveEmptyEntries) + .Select(o => o.RemovePostFix("/")) + .ToArray() + ) + .WithAbpExposedHeaders() + .SetIsOriginAllowedToAllowWildcardSubdomains() + .AllowAnyHeader() + .AllowAnyMethod() + .AllowCredentials(); }); + }); + + Configure(options => + { + options.WithOrigins( + configuration["App:CorsOrigins"] + .Split(",", StringSplitOptions.RemoveEmptyEntries) + .Select(o => o.RemovePostFix("/")) + .ToArray() + ); + }); // 支持本地化语言类型 Configure(options => @@ -171,7 +232,7 @@ namespace LINGYUN.Abp.MessageService options.Resources .Get() - .AddVirtualJson("/LINGYUN/Abp/MessageService/Localization/HttpApiHost"); + .AddVirtualJson("/Localization/HttpApiHost"); }); context.Services.AddAuthentication("Bearer") @@ -188,7 +249,7 @@ namespace LINGYUN.Abp.MessageService if (!hostingEnvironment.IsDevelopment()) { - var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]); + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); context.Services .AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "MessageService-Protection-Keys"); @@ -214,8 +275,12 @@ namespace LINGYUN.Abp.MessageService app.UseAbpRequestLocalization(); //路由 app.UseRouting(); + // 跨域 + app.UseCors(DefaultCorsPolicyName); // 加入自定义中间件 app.UseSignalRJwtToken(); + // TODO: 还有没有其他方法在iframe中传递身份令牌? + app.UseHangfireJwtToken(); // 认证 app.UseAuthentication(); // jwt @@ -234,7 +299,11 @@ namespace LINGYUN.Abp.MessageService // 审计日志 app.UseAuditing(); app.UseHangfireServer(); - app.UseHangfireDashboard(); + app.UseHangfireDashboard(options => + { + options.IgnoreAntiforgeryToken = true; + options.UseAuthorization(new HangfireDashboardAuthorizationFilter()); + }); // 路由 app.UseConfiguredEndpoints(); } diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Authorization/HangfireDashboardAuthorizationFilter.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Authorization/HangfireDashboardAuthorizationFilter.cs new file mode 100644 index 000000000..29862f433 --- /dev/null +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Authorization/HangfireDashboardAuthorizationFilter.cs @@ -0,0 +1,93 @@ +using Hangfire; +using Hangfire.Annotations; +using Hangfire.Dashboard; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using NUglify.Helpers; +using System.Linq; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Threading; + +namespace LINGYUN.Abp.MessageService.Authorization +{ + public class HangfireDashboardAuthorizationFilter : IDashboardAuthorizationFilter + { + protected string[] AllowGrantPath { get; } + public HangfireDashboardAuthorizationFilter() + { + AllowGrantPath = new string[] { "/css", "/js", "/fonts", "/stats" }; + } + + public bool Authorize([NotNull] DashboardContext context) + { + // 本地请求 + if (LocalRequestOnlyAuthorize(context)) + { + return true; + } + + // 放行路径 + if (AllowGrantPath.Contains(context.Request.Path)) + { + return true; + } + var httpContext = context.GetHttpContext(); + + var options = httpContext.RequestServices.GetService>()?.Value; + + if (options != null) + { + // 请求路径对应的权限检查 + // TODO: 怎么来传递用户身份令牌? + var permission = options.GetPermission(context.Request.Path); + if (!permission.IsNullOrWhiteSpace()) + { + var permissionChecker = httpContext.RequestServices.GetRequiredService(); + return AsyncHelper.RunSync(async () => await permissionChecker.IsGrantedAsync(permission)); + } + } + + return false; + } + + public override int GetHashCode() + { + // 类型相同就行了 + return GetType().FullName.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (obj == null) + { + return false; + } + // 类型相同就行了 + if (GetType().Equals(obj.GetType())) + { + return true; + } + return base.Equals(obj); + } + + protected virtual bool LocalRequestOnlyAuthorize(DashboardContext context) + { + if (string.IsNullOrEmpty(context.Request.RemoteIpAddress)) + { + return false; + } + + if (context.Request.RemoteIpAddress == "127.0.0.1" || context.Request.RemoteIpAddress == "::1") + { + return true; + } + + if (context.Request.RemoteIpAddress == context.Request.LocalIpAddress) + { + return true; + } + + return false; + } + } +} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/DashboardOptionsExtensions.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/DashboardOptionsExtensions.cs new file mode 100644 index 000000000..9b8374fe3 --- /dev/null +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/DashboardOptionsExtensions.cs @@ -0,0 +1,74 @@ +using Hangfire.Dashboard; +using JetBrains.Annotations; +using System.Collections.Generic; +using Volo.Abp; + +namespace Hangfire +{ + public static class DashboardOptionsExtensions + { + public static DashboardOptions AddAuthorization( + [NotNull] this DashboardOptions options, + [NotNull] IDashboardAuthorizationFilter authorizationFilter) + { + Check.NotNull(options, nameof(options)); + Check.NotNull(authorizationFilter, nameof(authorizationFilter)); + + List filters = new List(); + filters.AddRange(options.Authorization); + filters.AddIfNotContains(authorizationFilter); + + options.Authorization = filters; + + return options; + } + + public static DashboardOptions AddAuthorizations( + [NotNull] this DashboardOptions options, + [NotNull] IEnumerable authorizationFilters) + { + Check.NotNull(options, nameof(options)); + Check.NotNull(authorizationFilters, nameof(authorizationFilters)); + + List filters = new List(); + filters.AddRange(options.Authorization); + filters.AddIfNotContains(authorizationFilters); + + options.Authorization = filters; + + return options; + } + + public static DashboardOptions UseAuthorization( + [NotNull] this DashboardOptions options, + [NotNull] IDashboardAuthorizationFilter authorizationFilter) + { + Check.NotNull(options, nameof(options)); + Check.NotNull(authorizationFilter, nameof(authorizationFilter)); + + List filters = new List + { + authorizationFilter + }; + + options.Authorization = filters; + + return options; + } + + public static DashboardOptions UseAuthorizations( + [NotNull] this DashboardOptions options, + [NotNull] IEnumerable authorizationFilters) + { + Check.NotNull(options, nameof(options)); + Check.NotNull(authorizationFilters, nameof(authorizationFilters)); + + List filters = new List(); + filters.AddRange(authorizationFilters); + + options.Authorization = filters; + + return options; + } + } +} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireApplicationBuilderExtensions.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireApplicationBuilderExtensions.cs new file mode 100644 index 000000000..dedf71955 --- /dev/null +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireApplicationBuilderExtensions.cs @@ -0,0 +1,47 @@ +using JetBrains.Annotations; +using Microsoft.AspNetCore.Builder; +using System; +using Volo.Abp; + +namespace Hangfire +{ + public static class HangfireApplicationBuilderExtensions + { + public static IApplicationBuilder UseHangfireJwtToken( + [NotNull] this IApplicationBuilder app) + { + return app.UseMiddleware(); + } + + public static IApplicationBuilder UseHangfireDashboard( + [NotNull] this IApplicationBuilder app, + [CanBeNull] Action setup = null) + { + Check.NotNull(app, nameof(app)); + return app.UseHangfireDashboard("/hangfire", setup, null); + } + + public static IApplicationBuilder UseHangfireDashboard( + [NotNull] this IApplicationBuilder app, + [CanBeNull] string pathMatch = "/hangfire", + [CanBeNull] Action setup = null) + { + Check.NotNull(app, nameof(app)); + return app.UseHangfireDashboard(pathMatch, setup, null); + } + + public static IApplicationBuilder UseHangfireDashboard( + [NotNull] this IApplicationBuilder app, + [CanBeNull] string pathMatch = "/hangfire", + [CanBeNull] Action setup = null, + [CanBeNull] JobStorage storage = null) + { + Check.NotNull(app, nameof(app)); + + var options = new DashboardOptions(); + setup?.Invoke(options); + + return app.UseHangfireDashboard(pathMatch: pathMatch, options: options, storage: storage); + } + } +} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireDashboardRouteOptions.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireDashboardRouteOptions.cs new file mode 100644 index 000000000..ee9ea7213 --- /dev/null +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireDashboardRouteOptions.cs @@ -0,0 +1,55 @@ +using LINGYUN.Abp.MessageService.Permissions; +using System.Collections.Generic; +using System.Linq; + +namespace Hangfire +{ + public class HangfireDashboardRouteOptions + { + public IList AllowFrameOrigins { get; } + public IDictionary RoutePermissions { get; } + public HangfireDashboardRouteOptions() + { + AllowFrameOrigins = new List(); + RoutePermissions = new Dictionary(); + InitDefaultRoutes(); + } + + public void WithOrigins(params string[] origins) + { + AllowFrameOrigins.AddIfNotContains(origins); + } + + public void WithPermission(string route, string permission) + { + RoutePermissions.Add(route, permission); + } + + public string GetPermission(string route) + { + var permission = RoutePermissions + .Where(x => x.Key.StartsWith(route)) + .Select(x => x.Value) + .FirstOrDefault(); + + return permission; + } + + private void InitDefaultRoutes() + { + WithPermission("/hangfire", AbpMessageServicePermissions.Hangfire.Default); + WithPermission("/stats", AbpMessageServicePermissions.Hangfire.Default); + WithPermission("/servers", AbpMessageServicePermissions.Hangfire.Default); + WithPermission("/retries", AbpMessageServicePermissions.Hangfire.Default); + WithPermission("/recurring", AbpMessageServicePermissions.Hangfire.Default); + WithPermission("/jobs/enqueued", AbpMessageServicePermissions.Hangfire.ManageQueue); + WithPermission("/jobs/processing", AbpMessageServicePermissions.Hangfire.ManageQueue); + WithPermission("/jobs/scheduled", AbpMessageServicePermissions.Hangfire.ManageQueue); + WithPermission("/jobs/failed", AbpMessageServicePermissions.Hangfire.ManageQueue); + WithPermission("/jobs/deleted", AbpMessageServicePermissions.Hangfire.ManageQueue); + WithPermission("/jobs/awaiting", AbpMessageServicePermissions.Hangfire.ManageQueue); + WithPermission("/jobs/actions", AbpMessageServicePermissions.Hangfire.ManageQueue); + WithPermission("/jobs/details", AbpMessageServicePermissions.Hangfire.ManageQueue); + } + } +} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireJwtTokenMiddleware.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireJwtTokenMiddleware.cs new file mode 100644 index 000000000..708fc1407 --- /dev/null +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireJwtTokenMiddleware.cs @@ -0,0 +1,31 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using System.Collections.Generic; +using System.Threading.Tasks; +using Volo.Abp.DependencyInjection; + +namespace Hangfire +{ + public class HangfireJwtTokenMiddleware : IMiddleware, ITransientDependency + { + public async Task InvokeAsync(HttpContext context, RequestDelegate next) + { + // 通过 iframe 加载页面的话,需要手动传递 access_token 到参数列表 + if (context.Request.Path.StartsWithSegments("/hangfire") && context.User.Identity?.IsAuthenticated != true) + { + if (context.Request.Query.TryGetValue("access_token", out var accessTokens)) + { + context.Request.Headers.Add("Authorization", accessTokens); + } + var options = context.RequestServices.GetService>()?.Value; + if (options != null && options.AllowFrameOrigins.Count > 0) + { + // 跨域 iframe + context.Response.Headers.TryAdd("X-Frame-Options", $"\"ALLOW-FROM {options.AllowFrameOrigins.JoinAsString(",")}\""); + } + } + await next(context); + } + } +} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj index 7f323bee6..f1cb3d2c4 100644 --- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN.Abp.MessageService.HttpApi.Host.csproj @@ -6,13 +6,13 @@ - - + + - - + + diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Localization/HttpApiHost/en.json b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/en.json similarity index 57% rename from aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Localization/HttpApiHost/en.json rename to aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/en.json index 2716c4a9b..4c657e174 100644 --- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Localization/HttpApiHost/en.json +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/en.json @@ -2,6 +2,8 @@ "culture": "en", "texts": { "NewTenantRegisteredNotificationTitle": "Tenants create notification", - "NewTenantRegisteredNotificationMessage": "New tenants to create success, the tenant name: {0}!" + "NewTenantRegisteredNotificationMessage": "New tenants to create success, the tenant name: {0}!", + "Permission:Hangfire": "Hangfire dashboard", + "Permission:ManageQueue": "Manage queue" } } \ No newline at end of file diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Localization/HttpApiHost/zh-Hans.json b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/zh-Hans.json similarity index 59% rename from aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Localization/HttpApiHost/zh-Hans.json rename to aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/zh-Hans.json index 175a73b94..746d37822 100644 --- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Localization/HttpApiHost/zh-Hans.json +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Localization/HttpApiHost/zh-Hans.json @@ -2,6 +2,8 @@ "culture": "zh-Hans", "texts": { "NewTenantRegisteredNotificationTitle": "租户创建通知", - "NewTenantRegisteredNotificationMessage": "新租户创建成功,租户名称:{0}!" + "NewTenantRegisteredNotificationMessage": "新租户创建成功,租户名称:{0}!", + "Permission:Hangfire": "Hangfire仪表板", + "Permission:ManageQueue": "管理队列" } } \ No newline at end of file diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Notifications/MessageServiceDefinitionProvider.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Notifications/MessageServiceDefinitionProvider.cs similarity index 100% rename from aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Notifications/MessageServiceDefinitionProvider.cs rename to aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Notifications/MessageServiceDefinitionProvider.cs diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissionDefinitionProvider.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissionDefinitionProvider.cs new file mode 100644 index 000000000..c5ba7020a --- /dev/null +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissionDefinitionProvider.cs @@ -0,0 +1,22 @@ +using LINGYUN.Abp.MessageService.Localization; +using Volo.Abp.Authorization.Permissions; +using Volo.Abp.Localization; + +namespace LINGYUN.Abp.MessageService.Permissions +{ + public class AbpMessageServicePermissionDefinitionProvider : PermissionDefinitionProvider + { + public override void Define(IPermissionDefinitionContext context) + { + var group = context.GetGroup(MessageServicePermissions.GroupName); + + var hangfirePermission = group.AddPermission(AbpMessageServicePermissions.Hangfire.Default, L("Permission:Hangfire")); + hangfirePermission.AddChild(AbpMessageServicePermissions.Hangfire.ManageQueue, L("Permission:ManageQueue")); + } + + private static LocalizableString L(string name) + { + return LocalizableString.Create(name); + } + } +} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissions.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissions.cs new file mode 100644 index 000000000..ff2aeb98f --- /dev/null +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Permissions/AbpMessageServicePermissions.cs @@ -0,0 +1,12 @@ +namespace LINGYUN.Abp.MessageService.Permissions +{ + public class AbpMessageServicePermissions + { + public class Hangfire + { + public const string Default = MessageServicePermissions.GroupName + ".Hangfire"; + + public const string ManageQueue = Default + ".ManageQueue"; + } + } +} diff --git a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Utils/SnowflakeIdGenerator.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Utils/SnowflakeIdGenerator.cs similarity index 100% rename from aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/LINGYUN/Abp/MessageService/Utils/SnowflakeIdGenerator.cs rename to aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Utils/SnowflakeIdGenerator.cs diff --git a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/AppPlatformHttpApiHostModule.cs b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/AppPlatformHttpApiHostModule.cs index 1a23b6508..86d3e7a43 100644 --- a/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/AppPlatformHttpApiHostModule.cs +++ b/aspnet-core/services/platform/LINGYUN.Platform.HttpApi.Host/AppPlatformHttpApiHostModule.cs @@ -1,4 +1,5 @@ using DotNetCore.CAP; +using DotNetCore.CAP.Internal; using IdentityModel; using LINGYUN.Abp.Domain.Entities.Events; using LINGYUN.Abp.EventBus.CAP; @@ -121,7 +122,14 @@ namespace LINGYUN.Platform Configure(options => { // 加入需要处理的异常类型 - options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); + options.Handlers.Add(); }); // 自定义需要发送邮件通知的异常类型 Configure(options => @@ -130,8 +138,6 @@ namespace LINGYUN.Platform options.SendStackTrace = true; // 未指定异常接收者的默认接收邮件 options.DefaultReceiveEmail = "colin.in@foxmail.com"; - // 指定某种异常发送到哪个邮件 - options.HandReceivedException("colin.in@foxmail.com"); }); Configure(options => @@ -180,6 +186,25 @@ namespace LINGYUN.Platform options.SwaggerDoc("v1", new OpenApiInfo { Title = "Platform API", Version = "v1" }); options.DocInclusionPredicate((docName, description) => true); options.CustomSchemaIds(type => type.FullName); + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"", + Name = "Authorization", + In = ParameterLocation.Header, + Scheme = "bearer", + Type = SecuritySchemeType.Http, + BearerFormat = "JWT" + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" } + }, + new string[] { } + } + }); }); // 支持本地化语言类型 @@ -214,7 +239,7 @@ namespace LINGYUN.Platform if (!hostingEnvironment.IsDevelopment()) { - var redis = ConnectionMultiplexer.Connect(configuration["RedisCache:ConnectString"]); + var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]); context.Services .AddDataProtection() .PersistKeysToStackExchangeRedis(redis, "Platform-Protection-Keys"); diff --git a/vueJs/src/components/HangfireDashboard/index.vue b/vueJs/src/components/HangfireDashboard/index.vue new file mode 100644 index 000000000..d7e85a34f --- /dev/null +++ b/vueJs/src/components/HangfireDashboard/index.vue @@ -0,0 +1,49 @@ +