Włączanie żądań między źródłami (CORS) w ASP.NET Core

A którzy: Rick Anderson i Larkin

W tym artykule pokazano, jak włączyć funkcję CORS w ASP.NET Core aplikacji.

Zabezpieczenia przeglądarki uniemożliwiają stronie internetowej wykonywanie żądań do innej domeny niż ta, która obsługiwała stronę internetową. To ograniczenie jest nazywane zasadami tego samego źródła. Zasady tego samego źródła uniemożliwiają złośliwej witrynie odczytywanie poufnych danych z innej lokacji. Czasami można zezwolić innym witrynom na żądania między źródłami do aplikacji. Aby uzyskać więcej informacji, zobacz artykuł Mozilla CORS.

Współużytkuj zasoby między źródłami (CORS):

  • Jest standardem W3C, który umożliwia serwerowi złagodnianie zasad tego samego źródła.
  • Nie jest funkcją zabezpieczeń, a cors złagodnia zabezpieczenia. Interfejs API nie jest bezpieczniejszy, zezwalając na cors. Aby uzyskać więcej informacji, zobacz Jak działa cors.
  • Zezwala serwerowi na jawne zezwalanie na niektóre żądania między źródłami, jednocześnie odrzucając inne.
  • Jest bezpieczniejsze i bardziej elastyczne niż wcześniejsze techniki, takie jak JSONP.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

To samo źródło

Dwa adresy URL mają takie samo źródło, jeśli mają identyczne schematy, hosty i porty(RFC 6454).

Te dwa adresy URL mają takie samo źródło:

  • https://example.com/foo.html
  • https://example.com/bar.html

Te adresy URL mają inne źródła niż dwa poprzednie adresy URL:

  • https://example.net: inna domena
  • https://www.example.com/foo.html: inna poddomena
  • http://example.com/foo.html: inny schemat
  • https://example.com:9000/foo.html: inny port

Włączanie mechanizmu CORS

Istnieją trzy sposoby włączania funkcji CORS:

Użycie atrybutu [EnableCors] z nazwaną zasadą zapewnia pełną kontrolę nad ograniczaniem punktów końcowych, które obsługują cors.

Ostrzeżenie

UseCors Musi być wywoływana w prawidłowej kolejności. Aby uzyskać więcej informacji, zobacz Kolejność oprogramowania pośredniczącego. Na przykład, UseCors musi być wywoływana przed UseResponseCaching podczas korzystania z UseResponseCaching .

Każde podejście jest szczegółowo opisane w poniższych sekcjach.

CorS z nazwanych zasad i oprogramowania pośredniczącego

Oprogramowanie pośredniczące CORS obsługuje żądania między źródłami. Poniższy kod stosuje zasady CORS do wszystkich punktów końcowych aplikacji z określonymi źródłami:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors(MyAllowSpecificOrigins);

        // app.UseResponseCaching();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Powyższy kod ma następujące działanie:

W przypadku routingu punktów końcowych oprogramowanie pośredniczące CORS musi być skonfigurowane do wykonywania między wywołaniami do UseRouting i UseEndpoints .

Zobacz Testowanie kodu CORS, aby uzyskać instrukcje dotyczące testowania kodu podobnego do poprzedniego kodu.

Wywołanie AddCors metody dodaje usługi CORS do kontenera usługi aplikacji:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

Aby uzyskać więcej informacji, zobacz opcje zasad CORS w tym dokumencie.

Metody CorsPolicyBuilder mogą być łańcuchowe, jak pokazano w poniższym kodzie:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
    });

    services.AddControllers();
}

Uwaga: Określony adres URL nie może zawierać końcowego ukośnika ( / ). Jeśli adres URL kończy się na / , zostanie zwrócone porównanie false i nie zostanie zwrócony żaden nagłówek.

CorS z domyślnymi zasadami i oprogramowaniem pośredniczącem

Poniższy wyróżniony kod włącza domyślne zasady CORS:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });
        });

        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Poprzedni kod stosuje domyślne zasady CORS do wszystkich punktów końcowych kontrolera.

Włączanie cors z routingiem punktów końcowych

Włączenie mechanizmu CORS dla każdego punktu końcowego przy użyciu nie RequireCors obsługuje automatycznych żądań wstępnych. Aby uzyskać więcej informacji, zobacz ten GitHub i Przetestuj cors z routingiem punktów końcowych i [HttpOptions].

W przypadku routingu punktów końcowych można włączyć protokół CORS dla każdego punktu końcowego przy użyciu RequireCors zestawu metod rozszerzeń:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/echo",
                context => context.Response.WriteAsync("echo"))
                .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapControllers()
                     .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapGet("/echo2",
                context => context.Response.WriteAsync("echo2"));

            endpoints.MapRazorPages();
        });
    }
}

Powyższy kod ma następujące działanie:

  • app.UseCors włącza oprogramowanie pośredniczące CORS. Ponieważ zasady domyślne nie zostały skonfigurowane, app.UseCors() sam nie włącza cors.
  • Punkty /echo końcowe kontrolera i zezwalają na żądania między źródłami przy użyciu określonych zasad.
  • Punkty /echo2 końcowe i strony nie Razor zezwalają na żądania między źródłami, ponieważ nie określono zasad domyślnych.

Atrybut [DisableCors] nie wyłącza cors, który został włączony przez routing punktu końcowego za pomocą . RequireCors

Zobacz Testowanie cors z routingiem punktów końcowych i [HttpOptions], aby uzyskać instrukcje dotyczące testowania kodu podobnego do poprzedniego.

Włączanie cors z atrybutami

Włączenie funkcji CORS za pomocą atrybutu [EnableCors] i zastosowanie nazwanych zasad tylko do tych punktów końcowych, które wymagają cors, zapewnia doskonałą kontrolę.

Atrybut [EnableCors] stanowi alternatywę dla globalnego stosowania cors. Atrybut [EnableCors] włącza cors dla wybranych punktów końcowych, a nie dla wszystkich punktów końcowych:

  • [EnableCors] określa zasady domyślne.
  • [EnableCors("{Policy String}")] określa nazwane zasady.

Atrybut [EnableCors] można zastosować do:

  • Razor Strona PageModel
  • Kontroler
  • Metoda akcji kontrolera

Za pomocą atrybutu można zastosować różne zasady do kontrolerów, modeli stron lub metod [EnableCors] akcji. Gdy atrybut jest stosowany do kontrolera, modelu strony lub metody akcji, a funkcja CORS jest włączona w oprogramowanie [EnableCors] pośredniczące, stosowane są obie zasady. Zalecamy, aby nie łączyć zasad. Użyj [EnableCors] atrybutu lub oprogramowania pośredniczącego, a nie obu w tej samej aplikacji.

Poniższy kod stosuje różne zasady do każdej metody:

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors("Policy1")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return id switch
        {
            1 => "green widget",
            2 => "red widget",
            _ => NotFound(),
        };
    }
}

Poniższy kod tworzy dwie zasady CORS:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy("Policy1",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });

            options.AddPolicy("AnotherPolicy",
                builder =>
                {
                    builder.WithOrigins("http://www.contoso.com")
                                        .AllowAnyHeader()
                                        .AllowAnyMethod();
                });
        });

        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Aby uzyskać pełną kontrolę nad ograniczaniem żądań CORS:

  • Użyj [EnableCors("MyPolicy")] z nazwanych zasad.
  • Nie definiuj zasad domyślnych.
  • Nie używaj routingu punktów końcowych.

Kod w następnej sekcji jest zgodny z poprzednią listą.

Zobacz Testowanie kodu CORS, aby uzyskać instrukcje dotyczące testowania kodu podobnego do poprzedniego kodu.

Wyłączanie cors

Atrybut [DisableCors] nie wyłącza funkcji CORS, która została włączona przez routing punktu końcowego.

Poniższy kod definiuje zasady "MyPolicy" CORS:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
        });
    }
}

Poniższy kod wyłącza funkcję CORS dla GetValues2 akcji:

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

Powyższy kod ma następujące działanie:

Aby uzyskać instrukcje dotyczące testowania poprzedniego kodu, zobacz Testowanie kodu CORS.

Opcje zasad CORS

W tej sekcji opisano różne opcje, które można ustawić w zasadach CORS:

AddPolicy Jest wywoływana w Startup.ConfigureServices . W przypadku niektórych opcji warto najpierw przeczytać sekcję Jak działa cors.

Ustawianie dozwolonych źródeł

AllowAnyOrigin: zezwala na żądania CORS ze wszystkich źródeł przy użyciu dowolnego schematu ( http lub https ). AllowAnyOrigin jest niezabezpieczone, ponieważ każda witryna internetowa może do aplikacji zwracać żądania między źródłami.

Uwaga

Określenie wartości AllowAnyOrigin i AllowCredentials jest niezabezpieczoną konfiguracją, co może prowadzić do fałszowania żądań między witrynami. Usługa CORS zwraca nieprawidłową odpowiedź CORS, gdy aplikacja jest skonfigurowana przy użyciu obu metod.

AllowAnyOrigin wpływa na żądania wstępne i Access-Control-Allow-Origin nagłówek . Aby uzyskać więcej informacji, zobacz sekcję Żądania wstępne.

SetIsOriginAllowedToAllowWildcardSubdomains: ustawia właściwość zasad na funkcję, która umożliwia dopasowanie źródeł do skonfigurowanej domeny z symbolami wieloznacznymi podczas oceny, czy IsOriginAllowed źródło jest dozwolone.

options.AddPolicy("MyAllowSubdomainPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
            .SetIsOriginAllowedToAllowWildcardSubdomains();
    });

Ustawianie dozwolonych metod HTTP

AllowAnyMethod:

  • Zezwala na dowolną metodę HTTP:
  • Wpływa na żądania wstępne i Access-Control-Allow-Methods nagłówek . Aby uzyskać więcej informacji, zobacz sekcję Żądania wstępne.

Ustawianie dozwolonych nagłówków żądań

Aby zezwolić na określone nagłówki wysyłane w żądaniu CORS, nazywane nagłówkami żądania autora,wywołaj i określ WithHeaders dozwolone nagłówki:

options.AddPolicy("MyAllowHeadersPolicy",
    builder =>
    {
        // requires using Microsoft.Net.Http.Headers;
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

Aby zezwolić na wszystkie nagłówki żądania autora,wywołaj : AllowAnyHeader

options.AddPolicy("MyAllowAllHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

AllowAnyHeaderma wpływ na żądania wstępne i nagłówek Access-Control-Request-Headers. Aby uzyskać więcej informacji, zobacz sekcję Żądania wstępne.

Zasady oprogramowania pośredniczącego CORS są zgodne z określonymi nagłówkami określonymi przez usługę tylko wtedy, gdy nagłówki wysyłane w pliku są dokładnie zgodne z WithHeaders Access-Control-Request-Headers nagłówkami określonymi w pliku WithHeaders .

Rozważmy na przykład aplikację skonfigurowaną w następujący sposób:

app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));

Oprogramowanie pośredniczące CORS odrzuca żądanie wstępne z następującym nagłówkiem żądania, ponieważ Content-Language (HeaderNames.ContentLanguage) nie znajduje się na liście WithHeaders :

Access-Control-Request-Headers: Cache-Control, Content-Language

Aplikacja zwraca odpowiedź 200 OK, ale nie wysyła z powrotem nagłówków CORS. W związku z tym przeglądarka nie próbuje żądania między źródłami.

Ustawianie ujawnionych nagłówków odpowiedzi

Domyślnie przeglądarka nie uwidacznia wszystkich nagłówków odpowiedzi aplikacji. Aby uzyskać więcej informacji, zobacz W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.

Nagłówki odpowiedzi, które są domyślnie dostępne, to:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Specyfikacja CORS wywołuje następujące nagłówki prostych nagłówków odpowiedzi. Aby udostępnić inne nagłówki aplikacji, wywołaj wywołanie WithExposedHeaders :

options.AddPolicy("MyExposeResponseHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .WithExposedHeaders("x-custom-header");
    });

Poświadczenia w żądaniach między źródłami

Poświadczenia wymagają specjalnej obsługi w żądaniu CORS. Domyślnie przeglądarka nie wysyła poświadczeń z żądaniem między źródłami. Poświadczenia obejmują cookie schematy uwierzytelniania s i HTTP. Aby wysłać poświadczenia z żądaniem między źródłami, klient musi ustawić XMLHttpRequest.withCredentials wartość true .

Używanie XMLHttpRequest bezpośrednio:

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

Przy użyciu jQuery:

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

Przy użyciu interfejsu API pobierania:

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

Serwer musi zezwalać na poświadczenia. Aby zezwolić na poświadczenia między źródłami, wywołaj : AllowCredentials

options.AddPolicy("MyMyAllowCredentialsPolicy",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowCredentials();
    });

Odpowiedź HTTP zawiera nagłówek, który informuje przeglądarkę, że serwer zezwala na poświadczenia dla żądania Access-Control-Allow-Credentials między źródłami.

Jeśli przeglądarka wysyła poświadczenia, ale odpowiedź nie zawiera prawidłowego nagłówka, przeglądarka nie uwidacznia odpowiedzi aplikacji i żądanie między źródłami kończy się Access-Control-Allow-Credentials niepowodzeniem.

Zezwolenie na poświadczenia między źródłami stanowi zagrożenie bezpieczeństwa. Witryna internetowa w innej domenie może wysyłać poświadczenia zalogowaowego użytkownika do aplikacji w imieniu użytkownika bez wiedzy użytkownika.

Specyfikacja CORS określa również, że ustawienie wartości origins (wszystkie źródła) jest nieprawidłowe, "*" jeśli Access-Control-Allow-Credentials nagłówek jest obecny.

Żądania wstępne

W przypadku niektórych żądań CORS przeglądarka wysyła dodatkowe żądanie OPTIONS przed wysłaniem rzeczywistego żądania. To żądanie jest nazywane żądaniem wstępnym. Przeglądarka może pominąć żądanie wstępne, jeśli spełnione są wszystkie następujące warunki:

  • Metoda żądania to GET, HEAD lub POST.
  • Aplikacja nie ustawia nagłówków żądań innych niż Accept , , , ani Accept-Language Content-Language Content-Type Last-Event-ID .
  • Nagłówek, Content-Type jeśli został ustawiony, ma jedną z następujących wartości:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

Reguła dla nagłówków żądań ustawiona dla żądania klienta ma zastosowanie do nagłówków ustawianych przez aplikację przez wywołanie setRequestHeader dla XMLHttpRequest obiektu . Specyfikacja CORS wywołuje nagłówki żądania autora. Reguła nie ma zastosowania do nagłówków, które przeglądarka może ustawić, takich jak User-Agent Host , lub Content-Length .

Poniżej przedstawiono przykładową odpowiedź podobną do żądania wstępnego wykonanego za pomocą przycisku [Put test] w sekcji Test CORS tego dokumentu.

General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

Żądanie wstępne używa metody HTTP OPTIONS. Może ona zawierać następujące nagłówki:

Jeśli żądanie wstępne zostanie odrzucone, aplikacja zwróci odpowiedź, ale nie 200 OK ustawi nagłówków CORS. W związku z tym przeglądarka nie próbuje żądania między źródłami. Przykład odrzuconego żądania wstępnego można znaleźć w sekcji Test CORS tego dokumentu.

Przy użyciu narzędzi F12 w aplikacji konsolowej jest wyświetlany błąd podobny do jednego z następujących, w zależności od przeglądarki:

  • Firefox: żądanie między źródłami zablokowane: te same zasady źródła nie zezwalają na odczytywanie zasobu zdalnego https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 pod . (Przyczyna: Żądanie CORS nie powiodło się). Więcej informacji
  • Chromium: dostęp do pobrania w pliku " " ze źródła " " został zablokowany przez zasady CORS: Odpowiedź na żądanie wstępne nie przechodzi kontroli dostępu: dla żądanego zasobu nie ma nagłówka https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 https://cors3.azurewebsites.net "Access-Control-Allow-Origin". Jeśli nieprzezroczysta odpowiedź służy Twoim potrzebom, ustaw tryb żądania na wartość "no-cors", aby pobrać zasób z wyłączonym corsem.

Aby zezwolić na określone nagłówki, wywołaj : WithHeaders

options.AddPolicy("MyAllowHeadersPolicy",
    builder =>
    {
        // requires using Microsoft.Net.Http.Headers;
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

Aby zezwolić na wszystkie nagłówki żądania autora,wywołaj : AllowAnyHeader

options.AddPolicy("MyAllowAllHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

Przeglądarki nie są spójne w sposobu, w jaki ustawiają Access-Control-Request-Headers wartość . Jeśli:

  • Nagłówki są ustawione na wartość inną niż "*"
  • AllowAnyHeader jest wywoływana: uwzględnij co najmniej , i oraz wszelkie nagłówki Accept Content-Type Origin niestandardowe, które chcesz obsługiwać.

Automatyczny kod żądania wstępnego

Zastosowanie zasad CORS:

  • Globalnie przez wywołanie app.UseCors w . Startup.Configure
  • Przy użyciu [EnableCors] atrybutu .

ASP.NET Core odpowiada na wstępne żądanie OPTIONS.

Włączenie mechanizmu CORS dla każdego punktu końcowego przy użyciu aktualnie nie RequireCors obsługuje automatycznych żądań wstępnych.

W sekcji Testowanie cors tego dokumentu okazano to zachowanie.

Atrybut [HttpOptions] dla żądań wstępnych

Gdy funkcja CORS jest włączona przy użyciu odpowiednich zasad, ASP.NET Core zazwyczaj automatycznie odpowiada na żądania wstępne CORS. W niektórych scenariuszach może tak nie być. Na przykład przy użyciu cors z routingiem punktu końcowego.

Poniższy kod używa atrybutu [HttpOptions] do tworzenia punktów końcowych dla żądań OPTIONS:

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

Aby uzyskać instrukcje dotyczące testowania poprzedniego kodu, zobacz Testowanie cors z routingiem punktów końcowych i [HttpOptions].

Ustawianie czasu wygaśnięcia wstępnego

Nagłówek Access-Control-Max-Age określa, jak długo można buforować odpowiedź na żądanie wstępne. Aby ustawić ten nagłówek, wywołaj : SetPreflightMaxAge

options.AddPolicy("MySetPreflightExpirationPolicy",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
    });

Jak działa cors

W tej sekcji opisano, co się dzieje w żądaniu CORS na poziomie komunikatów HTTP.

  • CorS nie jest funkcją zabezpieczeń. CORS to standard W3C, który umożliwia serwerowi złagodnianie zasad tego samego źródła.
    • Na przykład złośliwy aktor może użyć skryptów między witrynami (XSS) względem witryny i wykonać żądanie między witrynami do swojej witryny z obsługą cors w celu kradzieży informacji.
  • Zezwalanie na cors nie zapewnia bezpieczniejszego interfejsu API.
    • To klient (przeglądarka) musi wymuszać zasady CORS. Serwer wykonuje żądanie i zwraca odpowiedź. Klient zwraca błąd i blokuje odpowiedź. Na przykład dowolne z następujących narzędzi wyświetli odpowiedź serwera:
  • Jest to sposób, aby serwer zezwalał przeglądarkom na wykonywanie żądania XHR między źródłami lub interfejsu API pobierania, które w przeciwnym razie byłoby zabronione.
    • Przeglądarki bez cors nie mogą wykonać żądań między źródłami. Przed cors, JSONP został użyty do obejścia tego ograniczenia. JSONP nie używa XHR, używa <script> tagu do odbierania odpowiedzi. Skrypty mogą być ładowane między źródłami.

Specyfikacja CORS wprowadziła kilka nowych nagłówków HTTP, które umożliwiają żądania między źródłami. Jeśli przeglądarka obsługuje funkcję CORS, automatycznie ustawia te nagłówki dla żądań między źródłami. Niestandardowy kod JavaScript nie jest wymagany do włączenia funkcji CORS.

Przycisk testu PUT we wdrożonym przykładzie

Poniżej przedstawiono przykład żądania między źródłami z przycisku Test wartości do https://cors1.azurewebsites.net/api/values . OriginNagłówek:

  • Udostępnia domenę witryny, która żąda.
  • Jest wymagany i musi różnić się od hosta.

Nagłówki ogólne

Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK

Nagłówki odpowiedzi

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

Nagłówki żądań

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...

W OPTIONS żądaniach serwer ustawia nagłówek Nagłówki odpowiedzi Access-Control-Allow-Origin: {allowed origin} w odpowiedzi. Na przykład wdrożone przykładowe żądanie przycisku Usuń [EnableCors] OPTIONS zawiera następujące nagłówki:

Nagłówki ogólne

Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content

Nagłówki odpowiedzi

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

Nagłówki żądań

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

W poprzednich nagłówkach Odpowiedzi serwer ustawia nagłówek Access-Control-Allow-Origin w odpowiedzi. Wartość https://cors1.azurewebsites.net tego nagłówka odpowiada Origin nagłówkowi z żądania.

Jeśli AllowAnyOrigin zostanie wywołana, zwracana jest wartość Access-Control-Allow-Origin: * symbolu wieloznacznego . AllowAnyOrigin zezwala na dowolne źródło.

Jeśli odpowiedź nie zawiera nagłówka , żądanie między źródłami Access-Control-Allow-Origin kończy się niepowodzeniem. W szczególności przeglądarka nie zezwala na żądanie. Nawet jeśli serwer zwróci pomyślną odpowiedź, przeglądarka nie udostępni odpowiedzi aplikacji klienckiej.

Wyświetlanie żądań OPTIONS

Domyślnie przeglądarki Chrome i Edge nie pokazują żądań OPTIONS na karcie sieci w narzędziach F12. Aby wyświetlić żądania OPTIONS w tych przeglądarkach:

  • chrome://flags/#out-of-blink-cors lub edge://flags/#out-of-blink-cors
  • wyłącz flagę .
  • Ponownie uruchomić.

Przeglądarka Firefox domyślnie wyświetla żądania OPTIONS.

CorS w usługach IIS

W przypadku wdrażania w usługach IIS mechanizm CORS musi zostać uruchomiony przed Windows uwierzytelnianiem, jeśli serwer nie został skonfigurowany do zezwalania na dostęp anonimowy. Aby obsługiwać ten scenariusz, należy zainstalować i skonfigurować moduł CORS usług IIS dla aplikacji.

Testowanie cors

Przykładowy plik do pobrania ma kod do testowania cors. Zobacz, jak pobrać plik. Przykład to projekt interfejsu API z Razor dodanymi stronami:

public class StartupTest2
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
        });
    }
}

Ostrzeżenie

WithOrigins("https://localhost:<port>"); powinien być używany tylko do testowania przykładowej aplikacji podobnej do przykładowego kodu pobierania.

Poniżej przedstawiono ValuesController punkty końcowe do testowania:

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

MyDisplayRouteInfo jest dostarczany przez pakiet Rick.Docs.Samples.RouteInfo NuGet wyświetla informacje o trasach.

Przetestuj poprzedni przykładowy kod przy użyciu jednej z następujących metod:

  • Użyj wdrożonej przykładowej aplikacji na stronie https://cors3.azurewebsites.net/ . Nie ma potrzeby pobierania przykładu.
  • Uruchom przykład przy dotnet run użyciu domyślnego adresu URL https://localhost:5001 .
  • Uruchom przykład z witryny Visual Studio z portem ustawionym na 44398, aby uzyskać adres URL https://localhost:44398 .

Używanie przeglądarki z narzędziami F12:

  • Wybierz przycisk Wartości i przejrzyj nagłówki na karcie Sieć.

  • Wybierz przycisk TESTUJ PUT. Zobacz Wyświetlanie żądań OPTIONS, aby uzyskać instrukcje dotyczące wyświetlania żądania OPTIONS. Test PUT tworzy dwa żądania: żądanie wstępne OPTIONS i żądanie PUT.

  • Wybierz GetValues2 [DisableCors] przycisk, aby wyzwolić nieudane żądanie CORS. Jak wspomniano w dokumencie, odpowiedź zwraca wartość 200 powodzenie, ale żądanie CORS nie jest dokonywane. Wybierz kartę Konsola, aby zobaczyć błąd CORS. W zależności od przeglądarki zostanie wyświetlony błąd podobny do następującego:

    Dostęp do pobierania ze źródła został zablokowany przez zasady CORS: dla żądanego zasobu nie ma nagłówka 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' "Access-Control-Allow-Origin". Jeśli nieprzezroczysta odpowiedź służy Twoim potrzebom, ustaw tryb żądania na wartość "no-cors", aby pobrać zasób z wyłączonym corsem.

Punkty końcowe z obsługą cors można testować za pomocą narzędzia, takiego jak curl, Fiddlerlub Postman. W przypadku korzystania z narzędzia źródło żądania określonego przez nagłówek musi różnić się od hosta Origin odbieraącego żądanie. Jeśli żądanie nie jest między źródłami na podstawie wartości Origin nagłówka:

  • Oprogramowanie pośredniczące CORS nie musi przetwarzać żądania.
  • Nagłówki CORS nie są zwracane w odpowiedzi.

Następujące polecenie używa polecenia curl w celu wystawienia żądania OPTIONS z informacjami:

curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

Testowanie cors z routingiem punktów końcowych i [HttpOptions]

Włączenie mechanizmu CORS dla każdego punktu końcowego przy użyciu aktualnie nie RequireCors obsługuje automatycznych żądań wstępnych. Rozważmy następujący kod, który używa routingu punktu końcowego w celu włączenia cors:

public class StartupEndPointBugTest
{
    readonly string MyPolicy = "_myPolicy";

    // .WithHeaders(HeaderNames.ContentType, "x-custom-header")
    // forces browsers to require a preflight request with GET

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyPolicy,
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com",
                                        "https://cors1.azurewebsites.net",
                                        "https://cors3.azurewebsites.net",
                                        "https://localhost:44398",
                                        "https://localhost:5001")
                           .WithHeaders(HeaderNames.ContentType, "x-custom-header")
                           .WithMethods("PUT", "DELETE", "GET", "OPTIONS");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers().RequireCors(MyPolicy);
            endpoints.MapRazorPages();
        });
    }
}

Poniżej przedstawiono TodoItems1Controller punkty końcowe do testowania:

[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
    // PUT: api/TodoItems1/5
    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return Content($"ID = {id}");
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // Delete: api/TodoItems1/5
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // GET: api/TodoItems1
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]
    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    // Delete: api/TodoItems1/MyDelete2/5
    [EnableCors]
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Przetestuj poprzedni kod ze strony testowej wdrożonego przykładu.

Przyciski Usuń [EnableCors] i GET [EnableCors] są dostępne pomyślnie, ponieważ punkty końcowe mają żądania wstępne i odpowiadają [EnableCors] na nie. Inne punkty końcowe nie będą działać. Przycisk GET kończy się niepowodzeniem, ponieważ skrypt JavaScript wysyła:

 headers: {
      "Content-Type": "x-custom-header"
 },

Poniżej przedstawiono TodoItems2Controller podobne punkty końcowe, ale zawiera jawny kod do odpowiadania na żądania OPTIONS:

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    [EnableCors]  // Rquired for this path
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]  // Rquired for this path
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Przetestuj poprzedni kod ze strony testowej wdrożonego przykładu. Z listy rozwijanej Kontroler wybierz pozycję Wstępne ustawianie, a następnie pozycję Ustaw kontroler. Wszystkie wywołania CORS do punktów TodoItems2Controller końcowych są pomyślnie.

Dodatkowe zasoby

A inicjatorzy: Rick Anderson iUjący Larkin

W tym artykule pokazano, jak włączyć funkcję CORS w ASP.NET Core aplikacji.

Zabezpieczenia przeglądarki uniemożliwiają stronie internetowej wykonywanie żądań do innej domeny niż ta, która obsługiwała stronę internetową. To ograniczenie jest nazywane zasadami tego samego źródła. Zasady tego samego źródła uniemożliwiają złośliwej witrynie odczytywanie poufnych danych z innej lokacji. Czasami możesz chcieć zezwolić innym witrynom na żądania między źródłami do Aplikacji. Aby uzyskać więcej informacji, zobacz artykuł Mozilla CORS.

Współużytkuj zasoby między źródłami (CORS):

  • Jest standardem W3C, który umożliwia serwerowi złagodnianie zasad tego samego źródła.
  • Nie jest funkcją zabezpieczeń, a cors złagodnia zabezpieczenia. Zezwalanie na cors nie zapewnia bezpieczniejszego interfejsu API. Aby uzyskać więcej informacji, zobacz Jak działa cors.
  • Zezwala serwerowi na jawne zezwalanie na niektóre żądania między źródłami, podczas gdy inne są odrzucane.
  • Jest bezpieczniejsza i bardziej elastyczna niż wcześniejsze techniki, takie jak JSONP.

Wyświetl lub pobierz przykładowy kod (jak pobrać)

To samo źródło

Dwa adresy URL mają takie samo źródło, jeśli mają identyczne schematy, hosty i porty(RFC 6454).

Te dwa adresy URL mają takie samo źródło:

  • https://example.com/foo.html
  • https://example.com/bar.html

Te adresy URL mają inne źródła niż dwa poprzednie adresy URL:

  • https://example.net: inna domena
  • https://www.example.com/foo.html: inna poddomena
  • http://example.com/foo.html: inny schemat
  • https://example.com:9000/foo.html: inny port

Włączanie mechanizmu CORS

Istnieją trzy sposoby włączania funkcji CORS:

Użycie atrybutu [EnableCors] z nazwaną zasadą zapewnia pełną kontrolę w ograniczaniu punktów końcowych, które obsługują cors.

Ostrzeżenie

UseCors Musi być wywoływana w prawidłowej kolejności. Aby uzyskać więcej informacji, zobacz Kolejność oprogramowania pośredniczącego. Na przykład, UseCors musi być wywoływana przed UseResponseCaching podczas korzystania z UseResponseCaching .

Każde podejście jest szczegółowo opisane w poniższych sekcjach.

CorS z nazwanych zasad i oprogramowania pośredniczącego

Oprogramowanie pośredniczące CORS obsługuje żądania między źródłami. Poniższy kod stosuje zasady CORS do wszystkich punktów końcowych aplikacji z określonymi źródłami:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors(MyAllowSpecificOrigins);

        // app.UseResponseCaching();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Powyższy kod ma następujące działanie:

W przypadku routingu punktów końcowych oprogramowanie pośredniczące CORS musi być skonfigurowane do wykonywania między wywołaniami do UseRouting i UseEndpoints .

Aby uzyskać instrukcje dotyczące testowania kodu podobnego do poprzedniego, zobacz Testowanie kodu CORS.

Wywołanie AddCors metody dodaje usługi CORS do kontenera usługi aplikacji:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        // services.AddResponseCaching();
        services.AddControllers();
    }

Aby uzyskać więcej informacji, zobacz opcje zasad CORS w tym dokumencie.

Metody CorsPolicyBuilder mogą być łańcuchowe, jak pokazano w poniższym kodzie:

public void ConfigureServices(IServiceCollection services)
{
    services.AddCors(options =>
    {
        options.AddPolicy(MyAllowSpecificOrigins,
                          builder =>
                          {
                              builder.WithOrigins("http://example.com",
                                                  "http://www.contoso.com")
                                                  .AllowAnyHeader()
                                                  .AllowAnyMethod();
                          });
    });

    services.AddControllers();
}

Uwaga: określony adres URL nie może zawierać końcowego ukośnika ( / ). Jeśli adres URL kończy się na / , porównanie zwraca wartość i nie jest false zwracany żaden nagłówek.

CorS z domyślnymi zasadami i oprogramowaniem pośredniczącem

Poniższy wyróżniony kod włącza domyślne zasady CORS:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddDefaultPolicy(
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });
        });

        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Poprzedni kod stosuje domyślne zasady CORS do wszystkich punktów końcowych kontrolera.

Włączanie cors z routingiem punktów końcowych

Włączenie mechanizmu CORS dla każdego punktu końcowego przy użyciu nie RequireCors obsługuje automatycznych żądań wstępnych. Aby uzyskać więcej informacji, zobacz ten GitHub i Przetestuj cors z routingiem punktu końcowego i [HttpOptions].

W przypadku routingu punktów końcowych można włączyć protokół CORS dla każdego punktu końcowego przy użyciu RequireCors zestawu metod rozszerzeń:

public class Startup
{
    readonly string MyAllowSpecificOrigins = "_myAllowSpecificOrigins";

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyAllowSpecificOrigins,
                              builder =>
                              {
                                  builder.WithOrigins("http://example.com",
                                                      "http://www.contoso.com");
                              });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/echo",
                context => context.Response.WriteAsync("echo"))
                .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapControllers()
                     .RequireCors(MyAllowSpecificOrigins);

            endpoints.MapGet("/echo2",
                context => context.Response.WriteAsync("echo2"));

            endpoints.MapRazorPages();
        });
    }
}

Powyższy kod ma następujące działanie:

  • app.UseCors włącza oprogramowanie pośredniczące CORS. Ponieważ zasady domyślne nie zostały skonfigurowane, app.UseCors() sam nie włącza cors.
  • Punkty /echo końcowe kontrolera i zezwalają na żądania między źródłami przy użyciu określonych zasad.
  • Punkty /echo2 końcowe i strony nie Razor zezwalają na żądania między źródłami, ponieważ nie określono zasad domyślnych.

Atrybut [DisableCors] nie wyłącza cors, który został włączony przez routing punktu końcowego za pomocą . RequireCors

Zobacz Testowanie cors z routingiem punktów końcowych i [HttpOptions], aby uzyskać instrukcje dotyczące testowania kodu podobnego do poprzedniego.

Włączanie cors z atrybutami

Włączenie cors z atrybutem [EnableCors] i zastosowanie nazwanych zasad tylko do tych punktów końcowych, które wymagają cors zapewnia pełną kontrolę.

Atrybut [EnableCors] stanowi alternatywę dla globalnego stosowania cors. Atrybut [EnableCors] włącza cors dla wybranych punktów końcowych, a nie dla wszystkich punktów końcowych:

  • [EnableCors] Określa zasady domyślne.
  • [EnableCors("{Policy String}")] określa nazwane zasady.

Atrybut [EnableCors] można zastosować do:

  • Razor Strona PageModel
  • Kontroler
  • Metoda akcji kontrolera

Różne zasady można stosować do kontrolerów, modeli stron lub metod akcji za pomocą [EnableCors] atrybutu . Gdy atrybut jest stosowany do kontrolera, modelu strony lub metody akcji, a w przypadku oprogramowania pośredniczącego jest włączony model [EnableCors] CORS, stosowane są obie zasady. Zalecamy, aby nie łączyć zasad. Użyj [EnableCors] atrybutu lub oprogramowania pośredniczącego, a nie obu w tej samej aplikacji.

Poniższy kod stosuje inne zasady do każdej metody:

[Route("api/[controller]")]
[ApiController]
public class WidgetController : ControllerBase
{
    // GET api/values
    [EnableCors("AnotherPolicy")]
    [HttpGet]
    public ActionResult<IEnumerable<string>> Get()
    {
        return new string[] { "green widget", "red widget" };
    }

    // GET api/values/5
    [EnableCors("Policy1")]
    [HttpGet("{id}")]
    public ActionResult<string> Get(int id)
    {
        return id switch
        {
            1 => "green widget",
            2 => "red widget",
            _ => NotFound(),
        };
    }
}

Poniższy kod tworzy dwie zasady CORS:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy("Policy1",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com");
                });

            options.AddPolicy("AnotherPolicy",
                builder =>
                {
                    builder.WithOrigins("http://www.contoso.com")
                                        .AllowAnyHeader()
                                        .AllowAnyMethod();
                });
        });

        services.AddControllers();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();

        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }
}

Aby uzyskać pełną kontrolę nad ograniczaniem żądań CORS:

  • Użyj [EnableCors("MyPolicy")] z nazwaną zasadą.
  • Nie należy definiować zasad domyślnych.
  • Nie używaj routingu punktu końcowego.

Kod w następnej sekcji jest zgodny z poprzednią listą.

Aby uzyskać instrukcje dotyczące testowania kodu podobnego do poprzedniego, zobacz Testowanie kodu CORS.

Wyłączanie cors

Atrybut [DisableCors] nie wyłącza cors, który został włączony przez routing punktu końcowego.

Poniższy kod definiuje zasady "MyPolicy" CORS:

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
        });
    }
}

Poniższy kod wyłącza funkcję CORS dla GetValues2 akcji:

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

Powyższy kod ma następujące działanie:

Aby uzyskać instrukcje dotyczące testowania poprzedniego kodu, zobacz Testowanie kodu CORS.

Opcje zasad CORS

W tej sekcji opisano różne opcje, które można ustawić w zasadach CORS:

AddPolicy Jest wywoływana w Startup.ConfigureServices . W przypadku niektórych opcji warto najpierw przeczytać sekcję Jak działa cors.

Ustawianie dozwolonych źródeł

AllowAnyOrigin: zezwala na żądania CORS ze wszystkich źródeł przy użyciu dowolnego schematu ( http lub https ). AllowAnyOrigin jest niezabezpieczone, ponieważ każda witryna internetowa może do aplikacji zwracać żądania między źródłami.

Uwaga

Określenie wartości AllowAnyOrigin i AllowCredentials jest niezabezpieczoną konfiguracją, co może prowadzić do fałszowania żądań między witrynami. Usługa CORS zwraca nieprawidłową odpowiedź CORS, gdy aplikacja jest skonfigurowana przy użyciu obu metod.

AllowAnyOrigin wpływa na żądania wstępne i Access-Control-Allow-Origin nagłówek . Aby uzyskać więcej informacji, zobacz sekcję Żądania wstępne.

SetIsOriginAllowedToAllowWildcardSubdomains: ustawia właściwość zasad na funkcję, która umożliwia dopasowanie źródeł do skonfigurowanej domeny z symbolami wieloznacznymi podczas oceny, czy IsOriginAllowed źródło jest dozwolone.

options.AddPolicy("MyAllowSubdomainPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
            .SetIsOriginAllowedToAllowWildcardSubdomains();
    });

Ustawianie dozwolonych metod HTTP

AllowAnyMethod:

  • Zezwala na dowolną metodę HTTP:
  • Wpływa na żądania wstępne i Access-Control-Allow-Methods nagłówek . Aby uzyskać więcej informacji, zobacz sekcję Żądania wstępne.

Ustawianie dozwolonych nagłówków żądań

Aby zezwolić na określone nagłówki wysyłane w żądaniu CORS, nazywane nagłówkami żądania autora,wywołaj i określ WithHeaders dozwolone nagłówki:

options.AddPolicy("MyAllowHeadersPolicy",
    builder =>
    {
        // requires using Microsoft.Net.Http.Headers;
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

Aby zezwolić na wszystkie nagłówki żądania autora,wywołaj : AllowAnyHeader

options.AddPolicy("MyAllowAllHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

AllowAnyHeaderma wpływ na żądania wstępne i nagłówek Access-Control-Request-Headers. Aby uzyskać więcej informacji, zobacz sekcję Żądania wstępne.

Zasady oprogramowania pośredniczącego CORS są zgodne z określonymi nagłówkami określonymi przez usługę tylko wtedy, gdy nagłówki wysyłane w pliku są dokładnie zgodne z WithHeaders Access-Control-Request-Headers nagłówkami określonymi w pliku WithHeaders .

Rozważmy na przykład aplikację skonfigurowaną w następujący sposób:

app.UseCors(policy => policy.WithHeaders(HeaderNames.CacheControl));

Oprogramowanie pośredniczące CORS odrzuca żądanie wstępne z następującym nagłówkiem żądania, ponieważ Content-Language (HeaderNames.ContentLanguage) nie znajduje się na liście WithHeaders :

Access-Control-Request-Headers: Cache-Control, Content-Language

Aplikacja zwraca odpowiedź 200 OK, ale nie wysyła z powrotem nagłówków CORS. W związku z tym przeglądarka nie próbuje żądania między źródłami.

Ustawianie ujawnionych nagłówków odpowiedzi

Domyślnie przeglądarka nie uwidacznia wszystkich nagłówków odpowiedzi aplikacji. Aby uzyskać więcej informacji, zobacz W3C Cross-Origin Resource Sharing (Terminology): Simple Response Header.

Nagłówki odpowiedzi, które są domyślnie dostępne, to:

  • Cache-Control
  • Content-Language
  • Content-Type
  • Expires
  • Last-Modified
  • Pragma

Specyfikacja CORS wywołuje następujące nagłówki prostych nagłówków odpowiedzi. Aby udostępnić inne nagłówki aplikacji, wywołaj wywołanie WithExposedHeaders :

options.AddPolicy("MyExposeResponseHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .WithExposedHeaders("x-custom-header");
    });

Poświadczenia w żądaniach między źródłami

Poświadczenia wymagają specjalnej obsługi w żądaniu CORS. Domyślnie przeglądarka nie wysyła poświadczeń z żądaniem między źródłami. Poświadczenia obejmują cookie schematy uwierzytelniania s i HTTP. Aby wysłać poświadczenia z żądaniem między źródłami, klient musi ustawić XMLHttpRequest.withCredentials wartość true .

Używanie XMLHttpRequest bezpośrednio:

var xhr = new XMLHttpRequest();
xhr.open('get', 'https://www.example.com/api/test');
xhr.withCredentials = true;

Przy użyciu jQuery:

$.ajax({
  type: 'get',
  url: 'https://www.example.com/api/test',
  xhrFields: {
    withCredentials: true
  }
});

Przy użyciu interfejsu API pobierania:

fetch('https://www.example.com/api/test', {
    credentials: 'include'
});

Serwer musi zezwalać na poświadczenia. Aby zezwolić na poświadczenia między źródłami, wywołaj : AllowCredentials

options.AddPolicy("MyMyAllowCredentialsPolicy",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .AllowCredentials();
    });

Odpowiedź HTTP zawiera nagłówek, który informuje przeglądarkę, że serwer zezwala na poświadczenia dla żądania Access-Control-Allow-Credentials między źródłami.

Jeśli przeglądarka wysyła poświadczenia, ale odpowiedź nie zawiera prawidłowego nagłówka, przeglądarka nie uwidacznia odpowiedzi aplikacji i żądanie między źródłami kończy się Access-Control-Allow-Credentials niepowodzeniem.

Zezwolenie na poświadczenia między źródłami stanowi zagrożenie bezpieczeństwa. Witryna internetowa w innej domenie może wysyłać poświadczenia zalogowaowego użytkownika do aplikacji w imieniu użytkownika bez wiedzy użytkownika.

Specyfikacja CORS określa również, że ustawienie wartości origins (wszystkie źródła) jest nieprawidłowe, "*" jeśli Access-Control-Allow-Credentials nagłówek jest obecny.

Żądania wstępne

W przypadku niektórych żądań CORS przeglądarka wysyła dodatkowe żądanie OPTIONS przed wysłaniem rzeczywistego żądania. To żądanie jest nazywane żądaniem wstępnym. Przeglądarka może pominąć żądanie wstępne, jeśli spełnione są wszystkie następujące warunki:

  • Metoda żądania to GET, HEAD lub POST.
  • Aplikacja nie ustawia nagłówków żądań innych niż Accept , , , ani Accept-Language Content-Language Content-Type Last-Event-ID .
  • Nagłówek, Content-Type jeśli został ustawiony, ma jedną z następujących wartości:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/plain

Reguła dla nagłówków żądań ustawiona dla żądania klienta ma zastosowanie do nagłówków ustawianych przez aplikację przez wywołanie setRequestHeader dla XMLHttpRequest obiektu . Specyfikacja CORS wywołuje nagłówki żądania autora. Reguła nie ma zastosowania do nagłówków, które przeglądarka może ustawić, takich jak User-Agent Host , lub Content-Length .

Poniżej przedstawiono przykładową odpowiedź podobną do żądania wstępnego wykonanego za pomocą przycisku [Put test] w sekcji Test CORS tego dokumentu.

General:
Request URL: https://cors3.azurewebsites.net/api/values/5
Request Method: OPTIONS
Status Code: 204 No Content

Response Headers:
Access-Control-Allow-Methods: PUT,DELETE,GET
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f8...8;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Vary: Origin

Request Headers:
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Method: PUT
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

Żądanie wstępne używa metody HTTP OPTIONS. Może ona zawierać następujące nagłówki:

Jeśli żądanie wstępne zostanie odrzucone, aplikacja zwróci odpowiedź, ale nie 200 OK ustawi nagłówków CORS. W związku z tym przeglądarka nie próbuje żądania między źródłami. Przykład odrzuconego żądania wstępnego można znaleźć w sekcji Test CORS tego dokumentu.

Przy użyciu narzędzi F12 w aplikacji konsolowej jest wyświetlany błąd podobny do jednego z następujących, w zależności od przeglądarki:

  • Firefox: żądanie między źródłami zablokowane: te same zasady źródła nie zezwalają na odczytywanie zasobu zdalnego https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 pod . (Przyczyna: Żądanie CORS nie powiodło się). Więcej informacji
  • Chromium: dostęp do pobrania w pliku " " ze źródła " " został zablokowany przez zasady CORS: Odpowiedź na żądanie wstępne nie przechodzi kontroli dostępu: dla żądanego zasobu nie ma nagłówka https://cors1.azurewebsites.net/api/TodoItems1/MyDelete2/5 https://cors3.azurewebsites.net "Access-Control-Allow-Origin". Jeśli nieprzezroczysta odpowiedź służy Twoim potrzebom, ustaw tryb żądania na wartość "no-cors", aby pobrać zasób z wyłączonym corsem.

Aby zezwolić na określone nagłówki, wywołaj : WithHeaders

options.AddPolicy("MyAllowHeadersPolicy",
    builder =>
    {
        // requires using Microsoft.Net.Http.Headers;
        builder.WithOrigins("http://example.com")
               .WithHeaders(HeaderNames.ContentType, "x-custom-header");
    });

Aby zezwolić na wszystkie nagłówki żądania autora,wywołaj : AllowAnyHeader

options.AddPolicy("MyAllowAllHeadersPolicy",
    builder =>
    {
        builder.WithOrigins("https://*.example.com")
               .AllowAnyHeader();
    });

Przeglądarki nie są spójne w sposobu, w jaki ustawiają Access-Control-Request-Headers wartość . Jeśli:

  • Nagłówki są ustawione na wartość inną niż "*"
  • AllowAnyHeader jest wywoływana: uwzględnij co najmniej , i oraz wszelkie nagłówki Accept Content-Type Origin niestandardowe, które chcesz obsługiwać.

Automatyczny kod żądania wstępnego

Zastosowanie zasad CORS:

  • Globalnie przez wywołanie app.UseCors w . Startup.Configure
  • Przy użyciu [EnableCors] atrybutu .

ASP.NET Core odpowiada na wstępne żądanie OPTIONS.

Włączenie mechanizmu CORS dla każdego punktu końcowego przy użyciu aktualnie nie RequireCors obsługuje automatycznych żądań wstępnych.

W sekcji Testowanie cors tego dokumentu okazano to zachowanie.

Atrybut [HttpOptions] dla żądań wstępnych

Gdy funkcja CORS jest włączona przy użyciu odpowiednich zasad, ASP.NET Core odpowiada na żądania wstępne CORS automatycznie. W niektórych scenariuszach może tak nie być. Na przykład przy użyciu cors z routingiem punktu końcowego.

Poniższy kod używa atrybutu [HttpOptions] do tworzenia punktów końcowych dla żądań OPTIONS:

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

Aby uzyskać instrukcje dotyczące testowania poprzedniego kodu, zobacz Testowanie cors z routingiem punktów końcowych i [HttpOptions].

Ustawianie czasu wygaśnięcia wstępnego

Nagłówek Access-Control-Max-Age określa, jak długo można buforować odpowiedź na żądanie wstępne. Aby ustawić ten nagłówek, wywołaj : SetPreflightMaxAge

options.AddPolicy("MySetPreflightExpirationPolicy",
    builder =>
    {
        builder.WithOrigins("http://example.com")
               .SetPreflightMaxAge(TimeSpan.FromSeconds(2520));
    });

Jak działa cors

W tej sekcji opisano, co się dzieje w żądaniu CORS na poziomie komunikatów HTTP.

  • CorS nie jest funkcją zabezpieczeń. CORS to standard W3C, który umożliwia serwerowi złagodnianie zasad tego samego źródła.
    • Na przykład złośliwy aktor może użyć skryptów między witrynami (XSS) względem witryny i wykonać żądanie między witrynami do swojej witryny z obsługą cors w celu kradzieży informacji.
  • Zezwalanie na cors nie zapewnia bezpieczniejszego interfejsu API.
    • To klient (przeglądarka) musi wymuszać zasady CORS. Serwer wykonuje żądanie i zwraca odpowiedź. Klient zwraca błąd i blokuje odpowiedź. Na przykład dowolne z następujących narzędzi wyświetli odpowiedź serwera:
  • Jest to sposób, aby serwer zezwalał przeglądarkom na wykonywanie żądania XHR między źródłami lub interfejsu API pobierania, które w przeciwnym razie byłoby zabronione.
    • Przeglądarki bez cors nie mogą wykonać żądań między źródłami. Przed cors, JSONP został użyty do obejścia tego ograniczenia. JSONP nie używa XHR, używa <script> tagu do odbierania odpowiedzi. Skrypty mogą być ładowane między źródłami.

Specyfikacja CORS wprowadziła kilka nowych nagłówków HTTP, które umożliwiają żądania między źródłami. Jeśli przeglądarka obsługuje funkcję CORS, automatycznie ustawia te nagłówki dla żądań między źródłami. Niestandardowy kod JavaScript nie jest wymagany do włączenia funkcji CORS.

Przycisk testu PUT we wdrożonym przykładzie

Poniżej przedstawiono przykład żądania między źródłami z przycisku Test wartości do https://cors1.azurewebsites.net/api/values . OriginNagłówek:

  • Udostępnia domenę witryny, która żąda.
  • Jest wymagany i musi różnić się od hosta.

Nagłówki ogólne

Request URL: https://cors1.azurewebsites.net/api/values
Request Method: GET
Status Code: 200 OK

Nagłówki odpowiedzi

Content-Encoding: gzip
Content-Type: text/plain; charset=utf-8
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors1.azurewebsites.net
Transfer-Encoding: chunked
Vary: Accept-Encoding
X-Powered-By: ASP.NET

Nagłówki żądań

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Connection: keep-alive
Host: cors1.azurewebsites.net
Origin: https://cors3.azurewebsites.net
Referer: https://cors3.azurewebsites.net/
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0 ...

W OPTIONS żądaniach serwer ustawia nagłówek Nagłówki odpowiedzi Access-Control-Allow-Origin: {allowed origin} w odpowiedzi. Na przykład wdrożone przykładowe żądanie przycisku Usuń [EnableCors] OPTIONS zawiera następujące nagłówki:

Nagłówki ogólne

Request URL: https://cors3.azurewebsites.net/api/TodoItems2/MyDelete2/5
Request Method: OPTIONS
Status Code: 204 No Content

Nagłówki odpowiedzi

Access-Control-Allow-Headers: Content-Type,x-custom-header
Access-Control-Allow-Methods: PUT,DELETE,GET,OPTIONS
Access-Control-Allow-Origin: https://cors1.azurewebsites.net
Server: Microsoft-IIS/10.0
Set-Cookie: ARRAffinity=8f...;Path=/;HttpOnly;Domain=cors3.azurewebsites.net
Vary: Origin
X-Powered-By: ASP.NET

Nagłówki żądań

Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9
Access-Control-Request-Headers: content-type
Access-Control-Request-Method: DELETE
Connection: keep-alive
Host: cors3.azurewebsites.net
Origin: https://cors1.azurewebsites.net
Referer: https://cors1.azurewebsites.net/test?number=2
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: cross-site
User-Agent: Mozilla/5.0

W poprzednich nagłówkach Odpowiedzi serwer ustawia nagłówek Access-Control-Allow-Origin w odpowiedzi. Wartość https://cors1.azurewebsites.net tego nagłówka odpowiada Origin nagłówkowi z żądania.

Jeśli AllowAnyOrigin zostanie wywołana, zwracana jest wartość Access-Control-Allow-Origin: * symbolu wieloznacznego . AllowAnyOrigin zezwala na dowolne źródło.

Jeśli odpowiedź nie zawiera nagłówka , żądanie między źródłami Access-Control-Allow-Origin kończy się niepowodzeniem. W szczególności przeglądarka nie zezwala na żądanie. Nawet jeśli serwer zwróci pomyślną odpowiedź, przeglądarka nie udostępni odpowiedzi aplikacji klienckiej.

Wyświetlanie żądań OPTIONS

Domyślnie przeglądarki Chrome i Edge nie pokazują żądań OPTIONS na karcie sieci w narzędziach F12. Aby wyświetlić żądania OPTIONS w tych przeglądarkach:

  • chrome://flags/#out-of-blink-cors lub edge://flags/#out-of-blink-cors
  • wyłącz flagę .
  • Ponownie uruchomić.

Przeglądarka Firefox domyślnie wyświetla żądania OPTIONS.

CorS w usługach IIS

W przypadku wdrażania w usługach IIS mechanizm CORS musi zostać uruchomiony przed Windows uwierzytelnianiem, jeśli serwer nie został skonfigurowany do zezwalania na dostęp anonimowy. Aby obsługiwać ten scenariusz, należy zainstalować i skonfigurować moduł CORS usług IIS dla aplikacji.

Testowanie cors

Przykładowy plik do pobrania ma kod do testowania cors. Zobacz, jak pobrać plik. Przykład to projekt interfejsu API z Razor dodanymi stronami:

public class StartupTest2
{
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: "MyPolicy",
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                        "http://www.contoso.com",
                        "https://cors1.azurewebsites.net",
                        "https://cors3.azurewebsites.net",
                        "https://localhost:44398",
                        "https://localhost:5001")
                            .WithMethods("PUT", "DELETE", "GET");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
            endpoints.MapRazorPages();
        });
    }
}

Ostrzeżenie

WithOrigins("https://localhost:<port>"); powinien być używany tylko do testowania przykładowej aplikacji podobnej do przykładowego kodu pobierania.

Poniżej przedstawiono ValuesController punkty końcowe do testowania:

[EnableCors("MyPolicy")]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
    // GET api/values
    [HttpGet]
    public IActionResult Get() =>
        ControllerContext.MyDisplayRouteInfo();

    // GET api/values/5
    [HttpGet("{id}")]
    public IActionResult Get(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // PUT api/values/5
    [HttpPut("{id}")]
    public IActionResult Put(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);


    // GET: api/values/GetValues2
    [DisableCors]
    [HttpGet("{action}")]
    public IActionResult GetValues2() =>
        ControllerContext.MyDisplayRouteInfo();

}

MyDisplayRouteInfo jest dostarczany przez pakiet Rick.Docs.Samples.RouteInfo NuGet wyświetla informacje o trasach.

Przetestuj poprzedni przykładowy kod przy użyciu jednej z następujących metod:

  • Użyj wdrożonej przykładowej aplikacji na stronie https://cors3.azurewebsites.net/ . Nie ma potrzeby pobierania przykładu.
  • Uruchom przykład przy dotnet run użyciu domyślnego adresu URL https://localhost:5001 .
  • Uruchom przykład z witryny Visual Studio z portem ustawionym na 44398, aby uzyskać adres URL https://localhost:44398 .

Używanie przeglądarki z narzędziami F12:

  • Wybierz przycisk Wartości i przejrzyj nagłówki na karcie Sieć.

  • Wybierz przycisk TESTUJ PUT. Zobacz Wyświetlanie żądań OPTIONS, aby uzyskać instrukcje dotyczące wyświetlania żądania OPTIONS. Test PUT tworzy dwa żądania: żądanie wstępne OPTIONS i żądanie PUT.

  • Wybierz GetValues2 [DisableCors] przycisk, aby wyzwolić nieudane żądanie CORS. Jak wspomniano w dokumencie, odpowiedź zwraca wartość 200 powodzenie, ale żądanie CORS nie jest dokonywane. Wybierz kartę Konsola, aby zobaczyć błąd CORS. W zależności od przeglądarki zostanie wyświetlony błąd podobny do następującego:

    Dostęp do pobierania ze źródła został zablokowany przez zasady CORS: dla żądanego zasobu nie ma nagłówka 'https://cors1.azurewebsites.net/api/values/GetValues2' 'https://cors3.azurewebsites.net' "Access-Control-Allow-Origin". Jeśli nieprzezroczysta odpowiedź służy Twoim potrzebom, ustaw tryb żądania na wartość "no-cors", aby pobrać zasób z wyłączonym corsem.

Punkty końcowe z obsługą cors można testować za pomocą narzędzia, takiego jak curl, Fiddlerlub Postman. W przypadku korzystania z narzędzia źródło żądania określonego przez nagłówek musi różnić się od hosta Origin odbieraącego żądanie. Jeśli żądanie nie jest między źródłami na podstawie wartości Origin nagłówka:

  • Oprogramowanie pośredniczące CORS nie musi przetwarzać żądania.
  • Nagłówki CORS nie są zwracane w odpowiedzi.

Następujące polecenie używa polecenia curl w celu wystawienia żądania OPTIONS z informacjami:

curl -X OPTIONS https://cors3.azurewebsites.net/api/TodoItems2/5 -i

Testowanie cors z routingiem punktów końcowych i [HttpOptions]

Włączenie mechanizmu CORS dla każdego punktu końcowego przy użyciu aktualnie nie RequireCors obsługuje automatycznych żądań wstępnych. Rozważmy następujący kod, który używa routingu punktu końcowego w celu włączenia cors:

public class StartupEndPointBugTest
{
    readonly string MyPolicy = "_myPolicy";

    // .WithHeaders(HeaderNames.ContentType, "x-custom-header")
    // forces browsers to require a preflight request with GET

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddCors(options =>
        {
            options.AddPolicy(name: MyPolicy,
                builder =>
                {
                    builder.WithOrigins("http://example.com",
                                        "http://www.contoso.com",
                                        "https://cors1.azurewebsites.net",
                                        "https://cors3.azurewebsites.net",
                                        "https://localhost:44398",
                                        "https://localhost:5001")
                           .WithHeaders(HeaderNames.ContentType, "x-custom-header")
                           .WithMethods("PUT", "DELETE", "GET", "OPTIONS");
                });
        });

        services.AddControllers();
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseRouting();

        app.UseCors();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers().RequireCors(MyPolicy);
            endpoints.MapRazorPages();
        });
    }
}

Poniżej przedstawiono TodoItems1Controller punkty końcowe do testowania:

[Route("api/[controller]")]
[ApiController]
public class TodoItems1Controller : ControllerBase
{
    // PUT: api/TodoItems1/5
    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return Content($"ID = {id}");
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // Delete: api/TodoItems1/5
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    // GET: api/TodoItems1
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]
    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    // Delete: api/TodoItems1/MyDelete2/5
    [EnableCors]
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Przetestuj poprzedni kod ze strony testowej wdrożonego przykładu.

Przyciski Usuń [EnableCors] i GET [EnableCors] są dostępne pomyślnie, ponieważ punkty końcowe mają żądania wstępne i odpowiadają [EnableCors] na nie. Inne punkty końcowe nie będą działać. Przycisk GET kończy się niepowodzeniem, ponieważ skrypt JavaScript wysyła:

 headers: {
      "Content-Type": "x-custom-header"
 },

Poniżej przedstawiono TodoItems2Controller podobne punkty końcowe, ale zawiera jawny kod do odpowiadania na żądania OPTIONS:

[Route("api/[controller]")]
[ApiController]
public class TodoItems2Controller : ControllerBase
{
    // OPTIONS: api/TodoItems2/5
    [HttpOptions("{id}")]
    public IActionResult PreflightRoute(int id)
    {
        return NoContent();
    }

    // OPTIONS: api/TodoItems2 
    [HttpOptions]
    public IActionResult PreflightRoute()
    {
        return NoContent();
    }

    [HttpPut("{id}")]
    public IActionResult PutTodoItem(int id)
    {
        if (id < 1)
        {
            return BadRequest();
        }

        return ControllerContext.MyDisplayRouteInfo(id);
    }

    // [EnableCors] // Not needed as OPTIONS path provided
    [HttpDelete("{id}")]
    public IActionResult MyDelete(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);

    [EnableCors]  // Rquired for this path
    [HttpGet]
    public IActionResult GetTodoItems() =>
        ControllerContext.MyDisplayRouteInfo();

    [HttpGet("{action}")]
    public IActionResult GetTodoItems2() =>
        ControllerContext.MyDisplayRouteInfo();

    [EnableCors]  // Rquired for this path
    [HttpDelete("{action}/{id}")]
    public IActionResult MyDelete2(int id) =>
        ControllerContext.MyDisplayRouteInfo(id);
}

Przetestuj poprzedni kod ze strony testowej wdrożonego przykładu. Z listy rozwijanej Kontroler wybierz pozycję Wstępne ustawianie, a następnie pozycję Ustaw kontroler. Wszystkie wywołania CORS do punktów TodoItems2Controller końcowych są pomyślnie.

Dodatkowe zasoby