<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 9726字,需瀏覽 20分鐘

           ·

          2020-07-28 17:15

          前言

          由于現(xiàn)代互聯(lián)網(wǎng)的飛速發(fā)展,我們?cè)陂_發(fā)現(xiàn)代 Web 應(yīng)用程序中,經(jīng)常需要考慮多種類型的客戶端訪問服務(wù)的情況;而這種情況放在15年前幾乎是不可想象的,在那個(gè)時(shí)代,我們更多的是考慮怎么把網(wǎng)頁(yè)快速友好的嵌套到服務(wù)代碼中,經(jīng)過服務(wù)器渲染后輸出HTML到客戶端,沒有 iOS,沒有 Android,沒有 UWP。更多的考慮是 防止 XSS,在當(dāng)時(shí)的環(huán)境下,XSS一度成為各個(gè)站長(zhǎng)的噩夢(mèng),甚至網(wǎng)站開發(fā)的基本要求都要加上:必須懂防 XSS 攻擊。

          CORS 定義

          言歸正傳,CORS(Cross-Origin Resource Sharing)是由 W3C 指定的標(biāo)準(zhǔn),其目的是幫助在各個(gè)站點(diǎn)間的資源共享。CORS 不是一項(xiàng)安全標(biāo)準(zhǔn),啟用 CORS 實(shí)際上是讓站點(diǎn)放寬了安全標(biāo)準(zhǔn);通過配置 CORS,可以允許配置中的請(qǐng)求源執(zhí)行允許/拒絕的動(dòng)作。

          在 .NETCore 中啟用 CORS

          在 .NETCore 中,已經(jīng)為我們集成好 CORS 組件 Microsoft.AspNetCore.Cors,在需要的時(shí)候引入該組件即可,Microsoft.AspNetCore.Cors 的設(shè)計(jì)非常的簡(jiǎn)潔,包括兩大部分的內(nèi)容,看圖:

          從上圖中我們可以看出,左邊是入口,是我們常見的 AddCors/UseCors,右邊是 CORS 的核心配置和驗(yàn)證,配置對(duì)象是 CorsPolicyBuilder 和 CorsPolicy,驗(yàn)證入口為 CorsService,中間件 CorsMiddleware 提供了攔截驗(yàn)證入口。

          CorsService 是整個(gè) CORS 的核心實(shí)現(xiàn),客戶端的請(qǐng)求流經(jīng)中間件或者AOP組件后,他們?cè)趦?nèi)部調(diào)用 CorsService 的相關(guān)驗(yàn)證方法,在 CorsService 內(nèi)部使用配置好的 PolicyName 拉去相關(guān)策略進(jìn)行請(qǐng)求驗(yàn)證,最終返回驗(yàn)證結(jié)果到客戶端。

          Microsoft.AspNetCore.Mvc.Cors

          通常情況下,我們會(huì)在 Startup 類中的 ConfigureServices(IServiceCollection services) 方法內(nèi)部調(diào)用 AddCors() 來啟用 CROS 策略,但是,該 AddCors() 并不是上圖中 CorsServiceCollectionExrensions 中的 AddCors 擴(kuò)展方法。

          實(shí)際上,在 ConfigureServices 中調(diào)用的 AddCors 是處于程序集 Microsoft.AspNetCore.Mvc.Cors ;在 Microsoft.AspNetCore.Mvc.Cors 內(nèi)部的擴(kuò)展方法 AddCors() 中,以 AOP 方式定義了對(duì) EnableCorsAttribute/DisableCorsAttributeAttribute 的攔截檢查。

          具體做法是在程序集 Microsoft.AspNetCore.Mvc.Cors 內(nèi)部,定義了類 CorsApplicationModelProvider ,當(dāng)我們調(diào)用 AddCors 擴(kuò)展方法的時(shí)候,將進(jìn)一步調(diào)用 CorsApplicationModelProvider.OnProvidersExecuting(ApplicationModelProviderContext context) 方法,從而執(zhí)行檢查 EnableCorsAttribute/DisableCorsAttributeAttribute 策略。

          所以,我們?cè)?ConfigureServices 中調(diào)用的 AddCore,其實(shí)是在該程序集內(nèi)部定義的類:MvcCorsMvcCoreBuilderExtensions 的擴(kuò)展方法,我們看 MvcCorsMvcCoreBuilderExtensions 的定義

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

          重點(diǎn)就在上面的 AddCorsServices(IServiceCollection services) 方法中, 在方法中調(diào)用了 CORS 的擴(kuò)展方法 AddCors()。

          那么我們就要問, CorsApplicationModelProvider 是在什么時(shí)候被初始化的呢?答案是在 startup 中 ConfigureServices(IServiceCollection services) 方法內(nèi)調(diào)用 services.AddControllers() 的時(shí)候。在AddControllers() 方法內(nèi)部,調(diào)用了 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 的執(zhí)行過程,下面我們就可以開始了解應(yīng)該怎么在 .NETCore 中使用 CORS 的策略了

          CORS 啟用的三種方式

          在 .NETCore 中,可以通過以下三種方式啟用 CORS

          1、使用默認(rèn)策略/命名策略的中間件的方式 2、終結(jié)點(diǎn)路由 + 命名策略 3、命名策略 + EnableCorsAttribute

          通過上面的三種方式,可以靈活在程序中控制請(qǐng)求源的走向,但是,殘酷的事實(shí)告訴我們,一般情況下,我們都是會(huì)對(duì)全站進(jìn)行 CORS。所以,現(xiàn)實(shí)情況就是在大部分的 Web 應(yīng)用程序中, CORS 已然成為皇帝的新裝,甚至有點(diǎn)累贅。

          CorsPolicyBuilder(CORS策略)

          通過上面的 CORS 思維導(dǎo)圖,我們已經(jīng)大概了解了 CORS 的整個(gè)結(jié)構(gòu)。由上圖我們知道,CorsPolicyBuilder 位于命名空間 Microsoft.AspNetCore.Cors.Infrastructure 中。在內(nèi)部提供了兩種基礎(chǔ)控制策略:全開/半開。這兩種策略都提供了基本的方法供開發(fā)者直接調(diào)用,非常的貼心。

          全開

          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);


          上面的策略定義從字面理解就可以知道其用途,實(shí)際上呢,他們的實(shí)現(xiàn)原理也是非常的簡(jiǎn)單。在 CorsPolicyBuilder 內(nèi)部維護(hù)著一個(gè) CorsPolicy 對(duì)象,當(dāng)你使用全開/半開方式配置策略的時(shí)候,builder 會(huì)將配置寫入內(nèi)部 CorsPolicy 中存儲(chǔ)備用。

          比如半開 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;}

          開始使用

          在理解了配置的過程后,我們就可以進(jìn)入真正的使用環(huán)節(jié)了,通過上面的學(xué)習(xí)我們知道,啟用 CORS 有三種方式,咱們一步一步來。

          使用默認(rèn)策略/命名策略的中間件的方式

          所謂的命名策略就是給你的策略起個(gè)名字,默認(rèn)策略就是沒有名字,所有的入口都使用同一個(gè)策略,下面的代碼演示了命名策略

          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(); });}

          上面的代碼演示了如何在站點(diǎn)中全局終結(jié)點(diǎn)啟用 CORS,首先聲明了命名策略 corsalloworgins ,然后將其用 AddCors() 添加到 CORS 中,最后使用 UseCors() 啟用該命名策略,需要注意的是,AddCors() 和 UseCors() 必須成對(duì)出現(xiàn),并且要使用同一個(gè)命名策略。

          終結(jié)點(diǎn)路由 + 命名策略

          .NETCore 支持通過對(duì)單個(gè)路由設(shè)置 CORS 命名策略,從而可以實(shí)現(xiàn)在一個(gè)系統(tǒng)中,對(duì)不同的業(yè)務(wù)提供個(gè)性化的支持。終結(jié)點(diǎn)路由 + 命名策略的配置和上面的命名策略基本相同,僅僅是在配置路由的時(shí)候,只需要對(duì)某個(gè)路由增加 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 需要執(zhí)行 CORS 策略 CORSALLOWORGINS。通過調(diào)用 RequireCors() 方法,傳入策略名稱,完成 CORS 的配置。RequireCors 方法是在程序集 Microsoft.AspNetCore.Cors 內(nèi)部的擴(kuò)展方法,具體是怎么啟用策略的呢,其實(shí)就是在內(nèi)部給指定的終結(jié)點(diǎn)路由增加了 EnableCorsAttribute ,這就是下面要說到的第三種啟用 CORS 的方式。

          來看看 RequireCors() 內(nèi)部的代碼

          public static TBuilder RequireCors(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 特性標(biāo)記,和 RequireCors 方法內(nèi)部的實(shí)現(xiàn)不同的是,這里說的 EnableCorsAttribute 是顯式的指定到控制器上,在應(yīng)用 EnableCorsAttribute 的時(shí)候,你可以應(yīng)用到根控制器或者子控制器上,如果是對(duì)根控制器進(jìn)行標(biāo)記,被標(biāo)記的根控制器和他的所有子控制器都將受指定 CORS 策略的影響;反之,如果只是對(duì)子控制器進(jìn)行標(biāo)記,CORS 策略也只對(duì)當(dāng)前控制器產(chǎn)生影響。

          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(); });}

          在上面的代碼中,因?yàn)?EnableCorsAttribute 可以應(yīng)用到類和屬性上,所以我們定義了兩個(gè) CORS 策略,分別是 controllercors 和 actioncors。接下來將這兩種策略應(yīng)用到 WeatherForecastController 上。

          應(yīng)用 EnableCorsAttribute 特性標(biāo)記

          [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 標(biāo)記到控制器上,將 actioncors 標(biāo)記到 Action 名稱為 Users 上面,同時(shí),還對(duì) List 應(yīng)用了 DisableCors ,表示對(duì) List 禁用 CORS 的策略,所以我們知道,在 CORS 中,有 AddCors/UseCors,也有 EnableCors/DisableCors ,都是成對(duì)出現(xiàn)的。

          其它策略

          我們還記得,在 .NETCore 中,一共有 4 種策略,分別是:Header、Method、Origin、Credentials,但是本文僅演示了 WithOrigins 這一種方式,相信通過這一種方式的演示,對(duì)大家在啟用其它策略的時(shí)候,其思想也是一致的,所謂的標(biāo)頭、請(qǐng)求方式、憑據(jù) 等等,其基本法是不變的。

          通過對(duì) Microsoft.AspNetCore.Cors 的內(nèi)部實(shí)現(xiàn)的剖析,我們了解到,其實(shí)現(xiàn) CORS 的原理非常簡(jiǎn)單,結(jié)構(gòu)清晰,就算不用系統(tǒng)自帶的 CORS 組件,自行實(shí)現(xiàn)一個(gè) CORS 策略,也是非常容易的。

          參考資料:(CORS) 啟用跨域請(qǐng)求 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


          瀏覽 27
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色视频直接看 | 影音先锋亚洲成人 | 九九精品重口味三级在线视频 | 国产www视频 | 日本处女学生妺一级片 |