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/services/account/AuthServer.Host/AuthIdentityServerModule.cs b/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs index 289533348..b28fc772c 100644 --- a/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs +++ b/aspnet-core/services/account/AuthServer.Host/AuthIdentityServerModule.cs @@ -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/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs b/aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs index c9d5d9108..613c0558c 100644 --- a/aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs +++ b/aspnet-core/services/admin/LINGYUN.BackendAdminApp.Host/BackendAdminHostModule.cs @@ -306,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 e82316470..704648bf5 100644 --- a/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs +++ b/aspnet-core/services/apigateway/LINGYUN.ApiGateway.HttpApi.Host/ApiGatewayHttpApiHostModule.cs @@ -210,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/AbpMessageServiceHttpApiHostModule.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/AbpMessageServiceHttpApiHostModule.cs index a0750fd17..d12a51e50 100644 --- a/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/AbpMessageServiceHttpApiHostModule.cs +++ b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/AbpMessageServiceHttpApiHostModule.cs @@ -16,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; @@ -68,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(); @@ -190,6 +195,35 @@ namespace LINGYUN.Abp.MessageService }); }); + 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 => { @@ -241,8 +275,12 @@ namespace LINGYUN.Abp.MessageService app.UseAbpRequestLocalization(); //路由 app.UseRouting(); + // 跨域 + app.UseCors(DefaultCorsPolicyName); // 加入自定义中间件 app.UseSignalRJwtToken(); + // TODO: 还有没有其他方法在iframe中传递身份令牌? + app.UseHangfireJwtToken(); // 认证 app.UseAuthentication(); // jwt @@ -263,6 +301,7 @@ namespace LINGYUN.Abp.MessageService app.UseHangfireServer(); app.UseHangfireDashboard(options => { + options.IgnoreAntiforgeryToken = true; options.UseAuthorization(new HangfireDashboardAuthorizationFilter()); }); // 路由 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 index 298b561d2..29862f433 100644 --- 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 @@ -1,7 +1,10 @@ -using Hangfire.Annotations; +using Hangfire; +using Hangfire.Annotations; using Hangfire.Dashboard; -using LINGYUN.Abp.MessageService.Permissions; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using NUglify.Helpers; +using System.Linq; using Volo.Abp.Authorization.Permissions; using Volo.Abp.Threading; @@ -9,18 +12,42 @@ 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 permissionChecker = httpContext.RequestServices.GetService(); + var options = httpContext.RequestServices.GetService>()?.Value; - if (permissionChecker != null) + if (options != null) { - // 可以详细到每个页面授权,这里就免了 - return AsyncHelper.RunSync(async () => await permissionChecker.IsGrantedAsync(AbpMessageServicePermissions.Hangfire.ManageQueue)); + // 请求路径对应的权限检查 + // 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 new LocalRequestsOnlyAuthorizationFilter().Authorize(context); + + return false; } public override int GetHashCode() @@ -42,5 +69,25 @@ namespace LINGYUN.Abp.MessageService.Authorization } 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/HangfireApplicationBuilderExtensions.cs b/aspnet-core/services/messages/LINGYUN.Abp.MessageService.HttpApi.Host/Hangfire/HangfireApplicationBuilderExtensions.cs index 9b20af009..dedf71955 100644 --- 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 @@ -7,6 +7,12 @@ 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) 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/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 @@ +