深入剖析.NETCORE中CORS(跨站资源共享)

前言

由于现代互联网的飞速发展,我们在开发现代 Web 应用程序中,经常需要考虑多种类型的客户端访问服务的情况;而这种情况放在15年前几乎是不可想象的,在那个时代,我们更多的是考虑怎么把网页快速友好的嵌套到服务代码中,经过服务器渲染后输出HTML到客户端,没有 iOS,没有 Android,没有 UWP。更多的考虑是 防止 XSS,在当时的环境下,XSS一度成为各个站长的噩梦,甚至网站开发的基本要求都要加上:必须懂防 XSS 攻击。

CORS 定义

言归正传,CORS(Cross-Origin Resource Sharing)是由 W3C 指定的标准,其目的是帮助在各个站点间的资源共享。CORS 不是一项安全标准,启用 CORS 实际上是让站点放宽了安全标准;通过配置 CORS,可以允许配置中的请求源执行允许/拒绝的动作。

在 .NETCore 中启用 CORS

在 .NETCore 中,已经为我们集成好 CORS 组件 Microsoft.AspNetCore.Cors,在需要的时候引入该组件即可,Microsoft.AspNetCore.Cors 的设计非常的简洁,包括两大部分的内容,看图:

从上图中我们可以看出,左边是入口,是我们常见的 AddCors/UseCors,右边是 CORS 的核心配置和验证,配置对象是 CorsPolicyBuilder 和 CorsPolicy,验证入口为 CorsService,中间件 CorsMiddleware 提供了拦截验证入口。

CorsService 是整个 CORS 的核心实现,客户端的请求流经中间件或者AOP组件后,他们在内部调用 CorsService 的相关验证方法,在 CorsService 内部使用配置好的 PolicyName 拉去相关策略进行请求验证,最终返回验证结果到客户端。

Microsoft.AspNetCore.Mvc.Cors

通常情况下,我们会在 Startup 类中的 ConfigureServices(IServiceCollection services) 方法内部调用 AddCors() 来启用 CROS 策略,但是,该 AddCors() 并不是上图中 CorsServiceCollectionExrensions 中的 AddCors 扩展方法。

实际上,在 ConfigureServices 中调用的 AddCors 是处于程序集 Microsoft.AspNetCore.Mvc.Cors ;在 Microsoft.AspNetCore.Mvc.Cors 内部的扩展方法 AddCors() 中,以 AOP 方式定义了对 EnableCorsAttribute/DisableCorsAttributeAttribute 的拦截检查。

具体做法是在程序集 Microsoft.AspNetCore.Mvc.Cors 内部,定义了类 CorsApplicationModelProvider ,当我们调用 AddCors 扩展方法的时候,将进一步调用 CorsApplicationModelProvider.OnProvidersExecuting(ApplicationModelProviderContext context) 方法,从而执行检查 EnableCorsAttribute/DisableCorsAttributeAttribute 策略。

所以,我们在 ConfigureServices 中调用的 AddCore,其实是在该程序集内部定义的类:MvcCorsMvcCoreBuilderExtensions 的扩展方法,我们看 MvcCorsMvcCoreBuilderExtensions 的定义

public static class MvcCorsMvcCoreBuilderExtensions
{public static IMvcCoreBuilder AddCors(this IMvcCoreBuilder builder){...AddCorsServices(builder.Services);...}public static IMvcCoreBuilder AddCors(this IMvcCoreBuilder builder,Action<CorsOptions> setupAction){...AddCorsServices(builder.Services);...}public static IMvcCoreBuilder ConfigureCors(this IMvcCoreBuilder builder,Action<CorsOptions> setupAction){...}// Internal for testing.internal static void AddCorsServices(IServiceCollection services){services.AddCors();services.TryAddEnumerable(ServiceDescriptor.Transient<IApplicationModelProvider, CorsApplicationModelProvider>());services.TryAddTransient<CorsAuthorizationFilter, CorsAuthorizationFilter>();}
}

重点就在上面的 AddCorsServices(IServiceCollection services) 方法中, 在方法中调用了 CORS 的扩展方法 AddCors()。

那么我们就要问, CorsApplicationModelProvider 是在什么时候被初始化的呢?答案是在 startup 中 ConfigureServices(IServiceCollection services) 方法内调用 services.AddControllers() 的时候。在AddControllers() 方法内部,调用了 AddControllersCore 方法

private static IMvcCoreBuilder AddControllersCore(IServiceCollection services)
{// This method excludes all of the view-related services by default.return services.AddMvcCore().AddApiExplorer().AddAuthorization().AddCors().AddDataAnnotations().AddFormatterMappings();
}

理解了 CORS 的执行过程,下面我们就可以开始了解应该怎么在 .NETCore 中使用 CORS 的策略了

CORS 启用的三种方式

在 .NETCore 中,可以通过以下三种方式启用 CORS

1、使用默认策略/命名策略的中间件的方式 2、终结点路由 + 命名策略 3、命名策略 + EnableCorsAttribute

通过上面的三种方式,可以灵活在程序中控制请求源的走向,但是,残酷的事实告诉我们,一般情况下,我们都是会对全站进行 CORS。所以,现实情况就是在大部分的 Web 应用程序中, CORS 已然成为皇帝的新装,甚至有点累赘。

CorsPolicyBuilder(CORS策略)

通过上面的 CORS 思维导图,我们已经大概了解了 CORS 的整个结构。由上图我们知道,CorsPolicyBuilder 位于命名空间 Microsoft.AspNetCore.Cors.Infrastructure 中。在内部提供了两种基础控制策略:全开/半开。这两种策略都提供了基本的方法供开发者直接调用,非常的贴心。

全开

public CorsPolicyBuilder AllowAnyHeader();
public CorsPolicyBuilder AllowAnyMethod();
public CorsPolicyBuilder AllowAnyOrigin();
public CorsPolicyBuilder AllowCredentials();

半开

public CorsPolicyBuilder DisallowCredentials();
public CorsPolicyBuilder WithHeaders(params string[] headers);
public CorsPolicyBuilder WithMethods(params string[] methods);
public CorsPolicyBuilder WithOrigins(params string[] origins);

上面的策略定义从字面理解就可以知道其用途,实际上呢,他们的实现原理也是非常的简单。在 CorsPolicyBuilder 内部维护着一个 CorsPolicy 对象,当你使用全开/半开方式配置策略的时候,builder 会将配置写入内部 CorsPolicy 中存储备用。

比如半开 WithOrigins(params string[] origins);,通过迭代器将配置的源写入 _policy.Origins 中。

public CorsPolicyBuilder WithOrigins(params string[] origins)
{foreach (var origin in origins){var normalizedOrigin = GetNormalizedOrigin(origin);_policy.Origins.Add(normalizedOrigin);}return this;
}

开始使用

在理解了配置的过程后,我们就可以进入真正的使用环节了,通过上面的学习我们知道,启用 CORS 有三种方式,咱们一步一步来。

使用默认策略/命名策略的中间件的方式

所谓的命名策略就是给你的策略起个名字,默认策略就是没有名字,所有的入口都使用同一个策略,下面的代码演示了命名策略

private readonly string CORS_ALLOW_ORGINS = "cors_allow_orgins";public void ConfigureServices(IServiceCollection services)
{services.AddCors(options =>{options.AddPolicy(CORS_ALLOW_ORGINS, policy =>{policy.WithOrigins("http://localhost:5500", "http://localhost:8099");});});services.AddControllers().AddJsonOptions(options =>{options.JsonSerializerOptions.Converters.Add(new StringJsonConverter());});
}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseCors(CORS_ALLOW_ORGINS);app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});
}

上面的代码演示了如何在站点中全局终结点启用 CORS,首先声明了命名策略 corsalloworgins ,然后将其用 AddCors() 添加到 CORS 中,最后使用 UseCors() 启用该命名策略,需要注意的是,AddCors() 和 UseCors() 必须成对出现,并且要使用同一个命名策略。

终结点路由 + 命名策略

.NETCore 支持通过对单个路由设置 CORS 命名策略,从而可以实现在一个系统中,对不同的业务提供个性化的支持。终结点路由 + 命名策略的配置和上面的命名策略基本相同,仅仅是在配置路由的时候,只需要对某个路由增加 RequireCors 的配置即可

private readonly string CORS_ALLOW_ORGINS = "cors_allow_orgins";
public void ConfigureServices(IServiceCollection services)
{services.AddCors(options =>{options.AddPolicy(CORS_ALLOW_ORGINS, policy =>{policy.WithOrigins("http://localhost:5500", "http://localhost:8099");});});services.AddControllers().AddJsonOptions(options =>{options.JsonSerializerOptions.Converters.Add(new StringJsonConverter());});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseCors();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllerRoute("weatherforecast", "{controller=WeatherForecast}/{action=Get}").RequireCors(CORS_ALLOW_ORGINS);// endpoints.MapControllers();});
}

上面的代码,指定了路由 weatherforecast 需要执行 CORS 策略 CORSALLOWORGINS。通过调用 RequireCors() 方法,传入策略名称,完成 CORS 的配置。RequireCors 方法是在程序集 Microsoft.AspNetCore.Cors 内部的扩展方法,具体是怎么启用策略的呢,其实就是在内部给指定的终结点路由增加了 EnableCorsAttribute ,这就是下面要说到的第三种启用 CORS 的方式。

来看看 RequireCors() 内部的代码

public static TBuilder RequireCors<TBuilder>(this TBuilder builder, string policyName) where TBuilder : IEndpointConventionBuilder
{if (builder == null){throw new ArgumentNullException(nameof(builder));}builder.Add(endpointBuilder =>{endpointBuilder.Metadata.Add(new EnableCorsAttribute(policyName));});return builder;
}

命名策略 + EnableCorsAttribute

最后一种启用 CORS 的方式是使用 EnableCorsAttribute 特性标记,和 RequireCors 方法内部的实现不同的是,这里说的 EnableCorsAttribute 是显式的指定到控制器上,在应用 EnableCorsAttribute 的时候,你可以应用到根控制器或者子控制器上,如果是对根控制器进行标记,被标记的根控制器和他的所有子控制器都将受指定 CORS 策略的影响;反之,如果只是对子控制器进行标记,CORS 策略也只对当前控制器产生影响。

CORS 的初始化

public void ConfigureServices(IServiceCollection services)
{services.AddCors(options =>{options.AddPolicy("controller_cors", policy =>{policy.WithOrigins("http://localhost:5500", "http://localhost:8099");});options.AddPolicy("action_cors", policy =>{policy.WithOrigins("http://localhost:5500", "http://localhost:8099");});});services.AddControllers().AddJsonOptions(options =>{options.JsonSerializerOptions.Converters.Add(new StringJsonConverter());});
}// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseRouting();app.UseCors();app.UseAuthorization();app.UseEndpoints(endpoints =>{endpoints.MapControllers();});
}

在上面的代码中,因为 EnableCorsAttribute 可以应用到类和属性上,所以我们定义了两个 CORS 策略,分别是 controllercors 和 actioncors。接下来将这两种策略应用到 WeatherForecastController 上。

应用 EnableCorsAttribute 特性标记

[ApiController]
[Route("[controller]")]
[EnableCors("controller_cors")]
public class WeatherForecastController : ControllerBase
{[EnableCors("action_cors")][HttpPost]public string Users(){return "Users";}[DisableCors][HttpGet]public string List(){return "List";}[HttpGet]public string Index(){return "Index";}
}

在上面的 WeatherForecastController 控制器中,我们将 controllercors 标记到控制器上,将 actioncors 标记到 Action 名称为 Users 上面,同时,还对 List 应用了 DisableCors ,表示对 List 禁用 CORS 的策略,所以我们知道,在 CORS 中,有 AddCors/UseCors,也有 EnableCors/DisableCors ,都是成对出现的。

其它策略

我们还记得,在 .NETCore 中,一共有 4 种策略,分别是:Header、Method、Origin、Credentials,但是本文仅演示了 WithOrigins 这一种方式,相信通过这一种方式的演示,对大家在启用其它策略的时候,其思想也是一致的,所谓的标头、请求方式、凭据 等等,其基本法是不变的。

通过对 Microsoft.AspNetCore.Cors 的内部实现的剖析,我们了解到,其实现 CORS 的原理非常简单,结构清晰,就算不用系统自带的 CORS 组件,自行实现一个 CORS 策略,也是非常容易的。

参考资料:(CORS) 启用跨域请求 ASP.NET Core

GitHub:https://github.com/dotnet/aspnetcore/tree/master/src/Mvc/Mvc/src https://github.com/dotnet/aspnetcore/tree/master/src/Mvc/Mvc.Cors/src https://github.com/dotnet/aspnetcore/tree/master/src/Middleware/CORS/src

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308652.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ai进入轮廓模式怎么退出_详解AI中扩展、扩展外观、轮廓化描边、创建轮廓

详解AI中扩展、扩展外观、轮廓化描边、创建轮廓在学习AI软件中&#xff0c;有不少同学分不清扩展、扩展外观、轮廓化描边、创建轮廓这四个概念具体的功能区别&#xff0c;今天我们具体聊一下。先说“扩展”&#xff0c;扩展是把复杂物体拆分成最基本的路径。矢量物体在组合&…

[Redis6]Redis相关知识介绍

Redis介绍相关知识 端口6379 6379 是 "MERZ " 九宫格输入法对应的数字。Alessia Merz 是一位意大利舞女、女演员。 Redis 作者 Antirez 早年看电视节目&#xff0c;觉得 Merz 在节目中的一些话愚蠢可笑&#xff0c;Antirez 喜欢造“梗”用于平时和朋友们交流&#x…

【Power Automate】如何自动生成Word与PDF文件[上]

上半年已经悄悄溜走&#xff0c;因为疫情&#xff0c;大家似乎也很习惯于在家办公。作为业务人员&#xff0c;如何汇报自己的工作&#xff0c;让自己更多地学习和掌握数字化办公技巧至关重要。那么今天我们就来看一下在不使用代码的情况下&#xff0c;如何通过Power Automate自…

easyui datagrid 中怎么选中所有页面的数据_学会这5个Excel中常用技巧,可以准时下班去摆摊了...

Excel是大家常用的办公工具之一&#xff0c;虽说上手简单&#xff0c;但是想要精通还是要下一些功夫的。最近有些小伙伴在留言吐槽说Excel数据处理时很方便&#xff0c;但是操作起来还是挺费时间的&#xff0c;其实工作是离不开技巧的&#xff0c;今天要跟大家分享的5个Excel技…

五年了,别再把务虚会开 “虚” 了

这是头哥侃码的第210篇原创上个月&#xff0c;为了配合公司的半年度战略讨论会&#xff0c;我特意留出一个周六的时间&#xff0c;与几位Leader在公司的会议室里开了一次部门半年度务虚会。让我没想到的是&#xff0c;几位小伙伴在这次讨论过程中都表现得非常亢奋&#xff0c;所…

人工智能正在如何改变传统行业

做了这么多年的技术工作&#xff0c;也正好赶上了这一波的人工智能浪潮&#xff0c;有时候我总是不免在想&#xff0c;人工智能如何真正地融入到我们的日常工作和生活中&#xff0c;实现它应有的价值。大家可能不知道&#xff0c;人工智能其实最早在上个世纪五十年代就提出来了…

[Redis6]key键操作

我们先连接redis cd /usr/local/bin/ redis-cliRedis键(key) keys *查看当前库所有key (匹配&#xff1a;keys *1) exists key判断某个key是否存在 type key 查看你的key是什么类型 del key 删除指定的key数据 unlink key 根据value选择非阻塞删除 仅将keys从keyspace元数据…

继续分享 5 个实用的 vs 调试技巧

前言我在上一篇文章????《5 个非常实用的 vs 调试技巧》 中分享了 5 个我认为非常值得了解的 vs 调试技巧&#xff0c;本周继续分享 5 个很基础但同样实用的调试技巧。1. 条件断点作用简介&#xff1a;顾名思义&#xff0c;带条件的断点。满足条件才中断。条件断点非常非常…

[Redis6]常用数据类型_String字符串

Redis字符串(String) 简介 String是Redis最基本的类型&#xff0c;你可以理解成与Memcached一模一样的类型&#xff0c;一个key对应一个value。String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象。String类型是Redis最基本的数据…

在ASP.NET Core中创建自定义端点可视化图

在上篇文章中&#xff0c;我为构建自定义端点可视化图奠定了基础&#xff0c;正如我在第一篇文章中展示的那样。该图显示了端点路由的不同部分&#xff1a;文字值&#xff0c;参数&#xff0c;动词约束和产生结果的端点&#xff1a;在本文中&#xff0c;我将展示如何通过创建一…

[Redis6]常用数据类型_List列表

List列表 简介 单键多值 Redis 列表是简单的字符串列表&#xff0c;按照插入顺序排序。你可以添加一个元素到列表的头部&#xff08;左边&#xff09;或者尾部&#xff08;右边&#xff09;。 它的底层实际是个双向链表&#xff0c;对两端的操作性能很高&#xff0c;通过索…

[Redis6]常用数据类型_Set集合

Set集合 简介 Redis set对外提供的功能与list类似是一个列表的功能&#xff0c;特殊之处在于set是可以自动排重的&#xff0c;当你需要存储一个列表数据&#xff0c;又不希望出现重复数据时&#xff0c;set是一个很好的选择&#xff0c;并且set提供了判断某个成员是否在一个s…

来领.NET Core学习资料,7天整理了30多个G(适合各阶段.Net开发者)

干货分享2020/07/27大家好&#xff0c;我是CSDN的小黑&#xff0c;2020年的唯一跳槽季&#xff0c;金九银十马上到来&#xff0c;今天这波干货必须学习起来&#xff01;前后整理了半个月&#xff0c;从零基础到就业&#xff0c;进阶高级开发&#xff0c;走向架构&#xff0c;三…

[Redis6]常用数据结构_Hash哈希

Hash哈希 简介 Redis hash 是一个键值对集合。 Redis hash是一个string类型的field和value的映射表&#xff0c;hash特别适合用于存储对象。 类似Java里面的Map<String,Object> 用户ID为查找的key&#xff0c;存储的value用户对象包含姓名&#xff0c;年龄&#xff…

ASP.NET Core中的分布式缓存

ASP.NET Core中的分布式缓存在上一篇文章中[1]&#xff0c;我解释了如何使用内存缓存在ASP.NET Core应用程序中管理缓存。如果您的应用程序托管在单个服务器上&#xff0c;则可以使用这种类型的缓存。那.NET Core框架可以使用哪些工具在云中的分布式方案中进行缓存呢IDistribut…

marquee滚动起始位置_巧用喵影关键帧制作滚动水印,让视频小偷无可盗

视频创作者最奔溃瞬间&#xff0c;不是翻遍了所有网站还找不到合适的素材&#xff0c;也不是作品快完成了却发现电脑死机&#xff0c;而是自己呕心沥血做出来的视频被盗了&#xff0c;结果盗版视频的播放量还更高&#xff01;&#xff01;​理想的解决方法就是给视频打上水印。…

.NET Core + K8S + Loki 玩转日志聚合

Grafana loki1. Intro最近在了解日志聚合系统&#xff0c;正好前几天看到一篇文章《用了日志系统新贵Loki&#xff0c;ELK突然不香了&#xff01;》&#xff0c;所以就决定动手体验一下。本文就带大家快速了解下Loki&#xff0c;并简单介绍.NET Core如何集成Loki。2. Whats Lok…

[Redis6]常用数据类型_Zset有序集合

Zset有序集合 简介 Redis有序集合zset与普通集合set非常相似&#xff0c;是一个没有重复元素的字符串集合。 不同之处是有序集合的每个成员都关联了一个评分&#xff08;score&#xff09;,这个评分&#xff08;score&#xff09;被用来按照从最低分到最高分的方式排序集合中…

css 旋转45_CSS教程——第14期

警告&#xff01;本期内容建议CSS基础良好的人看&#xff0c;本期为实战篇&#xff0c;难度有点大哈喽大家好我是wawjf灰常抱歉我们咕咕了快两周&#xff0c;主要原因是我们的几位作者(加上我其实就两个)去夏令营学习了某重要东西&#xff0c;所以没时间更新&#xff0c;在这里…

使用Azure Blob Storage实现一个静态文件服务器

什么是Azure Blob StorageAzure Blob Storage是微软Azure的对象存储服务。国内的云一般叫OSS&#xff0c;是一种用来存储非结构化数据的服务&#xff0c;比如音频&#xff0c;视频&#xff0c;图片&#xff0c;文本等等。用户可以通过http在全球任意地方访问这些资源。这些资源…