refactor: 添加Cors

This commit is contained in:
Diego
2024-07-29 09:47:58 +08:00
parent 1dbbac2aa3
commit ad3cc3ebb6
7 changed files with 272 additions and 11 deletions
@@ -0,0 +1,49 @@
// 版权归百小僧及百签科技(广东)有限公司所有。
using Furion.CorsAccessor;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Builder;
/// <summary>
/// 跨域中间件拓展
/// </summary>
public static class CorsAccessorApplicationBuilderExtensions
{
/// <summary>
/// 添加跨域中间件
/// </summary>
/// <param name="app"></param>
/// <param name="corsPolicyBuilderHandler"></param>
/// <returns></returns>
public static IApplicationBuilder UseCorsAccessor(this IApplicationBuilder app, Action<CorsPolicyBuilder> corsPolicyBuilderHandler = default)
{
// 获取选项
var corsAccessorSettings = app.ApplicationServices.GetService<IOptions<CorsAccessorSettingsOptions>>().Value;
// 判断是否启用 SignalR 跨域支持
if (corsAccessorSettings.SignalRSupport == false)
{
// 配置跨域中间件
_ = corsPolicyBuilderHandler == null
? app.UseCors(corsAccessorSettings.PolicyName)
: app.UseCors(corsPolicyBuilderHandler);
}
else
{
// 配置跨域中间件
app.UseCors(builder =>
{
// 设置跨域策略
Penetrates.SetCorsPolicy(builder, corsAccessorSettings, true);
// 添加自定义配置
corsPolicyBuilderHandler?.Invoke(builder);
});
}
return app;
}
}
@@ -0,0 +1,60 @@
// 版权归百小僧及百签科技(广东)有限公司所有。
using Furion;
using Furion.CorsAccessor;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using ThingsGateway;
using ThingsGateway.Core;
namespace Microsoft.Extensions.DependencyInjection;
/// <summary>
/// 跨域访问服务拓展类
/// </summary>
public static class CorsAccessorServiceCollectionExtensions
{
/// <summary>
/// 配置跨域
/// </summary>
/// <param name="services">服务集合</param>
/// <param name="corsOptionsHandler"></param>
/// <param name="corsPolicyBuilderHandler"></param>
/// <returns>服务集合</returns>
public static IServiceCollection AddCorsAccessor(this IServiceCollection services, Action<CorsOptions> corsOptionsHandler = default, Action<CorsPolicyBuilder> corsPolicyBuilderHandler = default)
{
// 解决服务重复注册问题
if (services.Any(u => u.ServiceType == typeof(IConfigureOptions<CorsAccessorSettingsOptions>)))
{
return services;
}
// 添加跨域配置选项
services.TryAddSingleton<IConfigureOptions<CorsAccessorSettingsOptions>, Microsoft.Extensions.DependencyInjection.ConfigureOptions<CorsAccessorSettingsOptions>>();
// 获取选项
var corsAccessorSettings = App.Configuration.GetSection("CorsAccessorSettings").Get<CorsAccessorSettingsOptions>();
// 添加跨域服务
services.AddCors(options =>
{
// 添加策略跨域
options.AddPolicy(corsAccessorSettings.PolicyName, builder =>
{
// 设置跨域策略
Penetrates.SetCorsPolicy(builder, corsAccessorSettings);
// 添加自定义配置
corsPolicyBuilderHandler?.Invoke(builder);
});
// 添加自定义配置
corsOptionsHandler?.Invoke(options);
});
return services;
}
}
@@ -0,0 +1,82 @@
// 版权归百小僧及百签科技(广东)有限公司所有。
using Microsoft.AspNetCore.Cors.Infrastructure;
namespace Furion.CorsAccessor;
/// <summary>
/// 常量、公共方法配置类
/// </summary>
internal static class Penetrates
{
/// <summary>
/// 默认跨域导出响应头 Key
/// </summary>
/// <remarks>解决 ajax,XMLHttpRequest,axios 不能获取请求头问题</remarks>
private static readonly string[] _defaultExposedHeaders = new[]
{
"access-token",
"x-access-token",
"Content-Disposition"
};
/// <summary>
/// 设置跨域策略
/// </summary>
/// <param name="builder"></param>
/// <param name="corsAccessorSettings"></param>
/// <param name="isMiddleware"></param>
internal static void SetCorsPolicy(CorsPolicyBuilder builder, CorsAccessorSettingsOptions corsAccessorSettings, bool isMiddleware = false)
{
// 判断是否设置了来源,因为 AllowAnyOrigin 不能和 AllowCredentials一起公用
var isNotSetOrigins = corsAccessorSettings.WithOrigins == null || corsAccessorSettings.WithOrigins.Length == 0;
// https://docs.microsoft.com/zh-cn/aspnet/core/signalr/security?view=aspnetcore-6.0
var isSupportSignarlR = isMiddleware && corsAccessorSettings.SignalRSupport == true;
// 设置总是允许跨域源配置
builder.SetIsOriginAllowed(_ => true);
// 如果没有配置来源,则允许所有来源
if (isNotSetOrigins)
{
// 解决 SignarlR 不能配置允许所有源问题
if (!isSupportSignarlR) builder.AllowAnyOrigin();
}
else builder.WithOrigins(corsAccessorSettings.WithOrigins)
.SetIsOriginAllowedToAllowWildcardSubdomains();
// 如果没有配置请求标头,则允许所有表头,包含处理 SignarlR 情况
if ((corsAccessorSettings.WithHeaders == null || corsAccessorSettings.WithHeaders.Length == 0) || isSupportSignarlR) builder.AllowAnyHeader();
else builder.WithHeaders(corsAccessorSettings.WithHeaders);
// 如果没有配置任何请求谓词,则允许所有请求谓词
if (corsAccessorSettings.WithMethods == null || corsAccessorSettings.WithMethods.Length == 0) builder.AllowAnyMethod();
else
{
// 解决 SignarlR 必须允许 GET POST 问题
if (isSupportSignarlR)
{
builder.WithMethods(corsAccessorSettings.WithMethods.Concat(new[] { "GET", "POST" }).Distinct(StringComparer.OrdinalIgnoreCase).ToArray());
}
else builder.WithMethods(corsAccessorSettings.WithMethods);
}
// 配置跨域凭据,包含处理 SignarlR 情况
if ((corsAccessorSettings.AllowCredentials == true && !isNotSetOrigins) || isSupportSignarlR) builder.AllowCredentials();
// 配置响应头,如果前端不能获取自定义的 header 信息,必须配置该项,默认配置了 access-token 和 x-access-token,可取消默认行为
IEnumerable<string> exposedHeaders = corsAccessorSettings.FixedClientToken == true
? _defaultExposedHeaders
: Array.Empty<string>();
if (corsAccessorSettings.WithExposedHeaders != null && corsAccessorSettings.WithExposedHeaders.Length > 0)
{
exposedHeaders = exposedHeaders.Concat(corsAccessorSettings.WithExposedHeaders).Distinct(StringComparer.OrdinalIgnoreCase);
}
if (exposedHeaders.Any()) builder.WithExposedHeaders(exposedHeaders.ToArray());
// 设置预检过期时间,如果不设置默认为 24小时
builder.SetPreflightMaxAge(TimeSpan.FromSeconds(corsAccessorSettings.SetPreflightMaxAge ?? 24 * 60 * 60));
}
}
@@ -0,0 +1,72 @@
// 版权归百小僧及百签科技(广东)有限公司所有。
using Microsoft.Extensions.Configuration;
using System.ComponentModel.DataAnnotations;
namespace Furion.CorsAccessor;
/// <summary>
/// 跨域配置选项
/// </summary>
public sealed class CorsAccessorSettingsOptions
{
/// <summary>
/// 策略名称
/// </summary>
[Required]
public string PolicyName { get; set; }
/// <summary>
/// 允许来源域名,没有配置则允许所有来源
/// </summary>
public string[] WithOrigins { get; set; }
/// <summary>
/// 请求表头,没有配置则允许所有表头
/// </summary>
public string[] WithHeaders { get; set; }
/// <summary>
/// 设置客户端可获取的响应标头
/// </summary>
public string[] WithExposedHeaders { get; set; }
/// <summary>
/// 设置跨域允许请求谓词,没有配置则允许所有
/// </summary>
public string[] WithMethods { get; set; }
/// <summary>
/// 是否允许跨域请求中的凭据
/// </summary>
public bool? AllowCredentials { get; set; }
/// <summary>
/// 设置预检过期时间
/// </summary>
public int? SetPreflightMaxAge { get; set; }
/// <summary>
/// 修正前端无法获取 Token 问题
/// </summary>
public bool? FixedClientToken { get; set; }
/// <summary>
/// 启用 SignalR 跨域支持
/// </summary>
public bool? SignalRSupport { get; set; }
/// <summary>
/// 后期配置
/// </summary>
/// <param name="options"></param>
/// <param name="configuration"></param>
public void PostConfigure(CorsAccessorSettingsOptions options, IConfiguration configuration)
{
PolicyName ??= "App.Cors.Policy";
WithOrigins ??= Array.Empty<string>();
AllowCredentials ??= true;
FixedClientToken ??= true;
SignalRSupport ??= false;
}
}
+2 -10
View File
@@ -82,7 +82,7 @@ public class Program
});
}
builder.Services.AddCors();
builder.Services.AddCorsAccessor();
builder.WebHost.UseWebRoot("wwwroot");
//builder.WebHost.UseStaticWebAssets();
@@ -193,15 +193,7 @@ public class Program
app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = provider });
app.UseStaticFiles();
var cors = app.Configuration["AllowOrigins"]?.Split(',', StringSplitOptions.RemoveEmptyEntries);
if (cors?.Length > 0)
{
app.UseCors(builder => builder.WithOrigins()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials());
}
app.UseCorsAccessor();
app.UseAuthentication();
app.UseAuthorization();
@@ -26,7 +26,6 @@ public class SingleFilePublish : ISingleFilePublish
/// <returns></returns>
public Assembly[] IncludeAssemblies()
{
// 需要 Furion 框架扫描哪些程序集就写上去即可
return Array.Empty<Assembly>();
}
@@ -2,6 +2,13 @@
"urls": "http://*:5000",
"ConfigurationScanDirectories": [ "" ],
"IgnoreConfigurationFiles": [ "" ],
"CorsAccessorSettings": {
"WithExposedHeaders": [ "access-token", "x-access-token", "environment" ],
"SignalRSupport": true
},
//冗余配置
"ManagementOptions": {
"PrimaryUri": "127.0.0.1:7101", //主(备)站IP