Povolení požadavků z více zdrojů ve webovém rozhraní API ASP.NET 2

Autor: Mike Wasson

Tento obsah je určený pro předchozí verzi rozhraní .NET. Nový vývoj by měl používat ASP.NET Core. Další informace o používání webového rozhraní API a požadavků mezi zdroji (CORS) v ASP.NET Core najdete tady:

Zabezpečení prohlížečů brání webovým stránkám v odesílání požadavků AJAX na jinou doménu. Toto omezení se označuje jako zásady stejného původu a brání škodlivému webu ve čtení citlivých dat z jiného webu. Někdy ale můžete chtít povolit volání vašeho webového rozhraní API jiným webům.

Sdílení prostředků mezi zdroji (CORS) je standard W3C, který umožňuje serveru uvolnit zásady stejného zdroje. Pomocí CORS může server explicitně povolit některé požadavky mezi zdroji a jiné odmítnout. CORS je bezpečnější a flexibilnější než dřívější techniky, jako je JSONP. V tomto kurzu se dozvíte, jak v aplikaci webového rozhraní API povolit CORS.

Software použitý v tomto kurzu

Úvod

Tento kurz ukazuje podporu CORS ve webovém rozhraní API ASP.NET. Začneme tím, že vytvoříme dva ASP.NET projekty – jeden s názvem "WebService", který je hostitelem kontroleru webového rozhraní API, a druhý s názvem "WebClient", který volá WebService. Vzhledem k tomu, že tyto dvě aplikace jsou hostovány v různých doménách, je požadavek AJAX z Webového klienta na webovou službu požadavek mezi zdroji.

Zobrazuje webovou službu a webového klienta.

Co je "stejný původ"?

Dvě adresy URL mají stejný původ, pokud mají identická schémata, hostitele a porty. (RFC 6454)

Tyto dvě adresy URL mají stejný původ:

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

Tyto adresy URL mají jiný původ než předchozí dvě adresy:

  • http://example.net – Jiná doména
  • http://example.com:9000/foo.html – Jiný port
  • https://example.com/foo.html - Jiné schéma
  • http://www.example.com/foo.html – Jiná subdoména

Poznámka

Internet Explorer při porovnávání původů nebere v úvahu port.

Vytvoření projektu WebService

Poznámka

V této části se předpokládá, že už víte, jak vytvářet projekty webového rozhraní API. Pokud ne, přečtěte si Začínáme s webovým rozhraním API ASP.NET.

  1. Spusťte Visual Studio a vytvořte nový projekt ASP.NET Web Application (.NET Framework).

  2. V dialogovém okně Nová webová aplikace ASP.NET vyberte šablonu prázdného projektu. V části Přidat složky a základní odkazy pro zaškrtněte políčko Webové rozhraní API .

    Dialogové okno Nový projekt ASP.NET v sadě Visual Studio

  3. Přidejte kontroler webového rozhraní API s následujícím TestController kódem:

    using System.Net.Http;
    using System.Web.Http;
    
    namespace WebService.Controllers
    {
        public class TestController : ApiController
        {
            public HttpResponseMessage Get()
            {
                return new HttpResponseMessage()
                {
                    Content = new StringContent("GET: Test message")
                };
            }
    
            public HttpResponseMessage Post()
            {
                return new HttpResponseMessage()
                {
                    Content = new StringContent("POST: Test message")
                };
            }
    
            public HttpResponseMessage Put()
            {
                return new HttpResponseMessage()
                {
                    Content = new StringContent("PUT: Test message")
                };
            }
        }
    }
    
  4. Aplikaci můžete spustit místně nebo ji nasadit do Azure. (Pro snímky obrazovky v tomto kurzu se aplikace nasadí do Azure App Service Web Apps.) Pokud chcete ověřit, že webové rozhraní API funguje, přejděte na http://hostname/api/test/adresu , kde název hostitele je doména, do které jste aplikaci nasadili. Měl by se zobrazit text odpovědi GET: Testovací zpráva.

    Webový prohlížeč zobrazující testovací zprávu

Vytvoření projektu WebClient

  1. Vytvořte další projekt ASP.NET Web Application (.NET Framework) a vyberte šablonu projektu MVC . Volitelně můžete vybrat Změnit ověřování>bez ověřování. Pro účely tohoto kurzu nepotřebujete ověřování.

    Šablona MVC v dialogovém okně Nový ASP.NET projekt v sadě Visual Studio

  2. V Průzkumník řešení otevřete soubor Views/Home/Index.cshtml. Nahraďte kód v tomto souboru následujícím kódem:

    <div>
        <select id="method">
            <option value="get">GET</option>
            <option value="post">POST</option>
            <option value="put">PUT</option>
        </select>
        <input type="button" value="Try it" onclick="sendRequest()" />
        <span id='value1'>(Result)</span>
    </div>
    
    @section scripts {
    <script>
        // TODO: Replace with the URL of your WebService app
        var serviceUrl = 'http://mywebservice/api/test'; 
    
        function sendRequest() {
            var method = $('#method').val();
    
            $.ajax({
                type: method,
                url: serviceUrl
            }).done(function (data) {
                $('#value1').text(data);
            }).fail(function (jqXHR, textStatus, errorThrown) {
                $('#value1').text(jqXHR.responseText || textStatus);
            });
        }
    </script>
    }
    

    Pro proměnnou serviceUrl použijte identifikátor URI aplikace WebService.

  3. Spusťte aplikaci WebClient místně nebo ji publikujte na jiném webu.

Když kliknete na tlačítko Vyzkoušet, odešle se do aplikace Webové služby požadavek AJAX pomocí metody HTTP uvedené v rozevíracím seznamu (GET, POST nebo PUT). To vám umožní zkoumat různé požadavky mezi zdroji. Aplikace Webové služby v současné době cors nepodporuje, takže když kliknete na tlačítko, zobrazí se chyba.

Chyba Vyzkoušet v prohlížeči

Poznámka

Pokud watch provoz HTTP v nástroji, jako je Fiddler, uvidíte, že prohlížeč požadavek GET odešle a požadavek bude úspěšný, ale volání AJAX vrátí chybu. Je důležité si uvědomit, že zásady stejného původu nebrání prohlížeči v odeslání požadavku. Místo toho zabrání aplikaci v zobrazení odpovědi.

Ladicí program webu Fiddler zobrazující webové požadavky

Povolení CORS

Teď povolíme CORS v aplikaci Webové služby. Nejprve přidejte balíček NUGet CORS. V sadě Visual Studio v nabídce Nástroje vyberte Správce balíčků NuGet a pak vyberte Konzola Správce balíčků. V okně Konzola Správce balíčků zadejte následující příkaz:

Install-Package Microsoft.AspNet.WebApi.Cors

Tento příkaz nainstaluje nejnovější balíček a aktualizuje všechny závislosti, včetně základních knihoven webového rozhraní API. Pomocí příznaku -Version zacílte na konkrétní verzi. Balíček CORS vyžaduje webové rozhraní API 2.0 nebo novější.

Otevřete soubor App_Start/WebApiConfig.cs. Do metody WebApiConfig.Register přidejte následující kód:

using System.Web.Http;
namespace WebService
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // New code
            config.EnableCors();

            config.Routes.MapHttpRoute(
                name: "DefaultApi",
                routeTemplate: "api/{controller}/{id}",
                defaults: new { id = RouteParameter.Optional }
            );
        }
    }
}

Dále do třídy přidejte atribut TestController[EnableCors]:

using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Cors;

namespace WebService.Controllers
{
    [EnableCors(origins: "http://mywebclient.azurewebsites.net", headers: "*", methods: "*")]
    public class TestController : ApiController
    {
        // Controller methods not shown...
    }
}

Jako parametr origins použijte identifikátor URI, do kterého jste nasadili aplikaci WebClient. To umožňuje požadavky z webového klienta mezi zdroji, ale zároveň se zakřivují všechny ostatní požadavky mezi doménou. Později popíšeme parametry pro [EnableCors] podrobněji.

Na konec původní adresy URL nezahrnujte lomítko.

Znovu nasaďte aktualizovanou aplikaci WebService. Webového klienta nemusíte aktualizovat. Požadavek AJAX z webového klienta by teď měl proběhnout úspěšně. Všechny metody GET, PUT a POST jsou povolené.

Webový prohlížeč zobrazující zprávu o úspěšném testu

Jak CORS funguje

Tato část popisuje, co se stane s požadavkem CORS na úrovni zpráv HTTP. Je důležité pochopit, jak CORS funguje, abyste mohli správně nakonfigurovat atribut [EnableCors] a vyřešit potíže, pokud všechno nefunguje podle očekávání.

Specifikace CORS zavádí několik nových hlaviček HTTP, které umožňují požadavky mezi zdroji. Pokud prohlížeč podporuje CORS, nastaví tyto hlavičky automaticky pro požadavky mezi zdroji; V kódu JavaScriptu nemusíte dělat nic zvláštního.

Tady je příklad požadavku mezi zdroji. Hlavička "Origin" poskytuje doménu webu, který vytváří požadavek.

GET http://myservice.azurewebsites.net/api/test HTTP/1.1
Referer: http://myclient.azurewebsites.net/
Accept: */*
Accept-Language: en-US
Origin: http://myclient.azurewebsites.net
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net

Pokud server požadavek povolí, nastaví hlavičku Access-Control-Allow-Origin. Hodnota této hlavičky buď odpovídá hlavičce Origin, nebo se jedná o hodnotu se zástupným znakem "*", což znamená, že jakýkoli původ je povolen.

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: text/plain; charset=utf-8
Access-Control-Allow-Origin: http://myclient.azurewebsites.net
Date: Wed, 05 Jun 2013 06:27:30 GMT
Content-Length: 17

GET: Test message

Pokud odpověď neobsahuje hlavičku Access-Control-Allow-Origin, požadavek AJAX selže. Konkrétně prohlížeč požadavek nepovoluje. I když server vrátí úspěšnou odpověď, prohlížeč odpověď klientské aplikaci nezpřístupní.

Předběžné požadavky

U některých požadavků CORS prohlížeč odešle před odesláním skutečné žádosti o prostředek další požadavek označovaný jako "předběžný požadavek".

Prohlížeč může předběžný požadavek přeskočit, pokud jsou splněny následující podmínky:

  • Metoda požadavku je GET, HEAD nebo POST a

  • Aplikace nenastavuje žádné jiné hlavičky požadavků než Accept, Accept-Language, Content-Language, Content-Type nebo Last-Event-ID a

  • Hlavička Content-Type (pokud je nastavená) je jedna z následujících možností:

    • application/x-www-form-urlencoded
    • multipart/form-data
    • text/prostý text

Pravidlo týkající se hlaviček požadavků se vztahuje na hlavičky, které aplikace nastaví voláním setRequestHeader v objektu XMLHttpRequest . (Specifikace CORS označuje tyto hlavičky žádostí o autory.) Toto pravidlo se nevztahuje na hlavičky, které může prohlížeč nastavit, například User-Agent, Host nebo Content-Length.

Tady je příklad předběžného požadavku:

OPTIONS http://myservice.azurewebsites.net/api/test HTTP/1.1
Accept: */*
Origin: http://myclient.azurewebsites.net
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: accept, x-my-custom-header
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)
Host: myservice.azurewebsites.net
Content-Length: 0

Předletová žádost používá metodu HTTP OPTIONS. Obsahuje dvě speciální hlavičky:

  • Access-Control-Request-Method: Metoda HTTP, která se použije pro vlastní požadavek.
  • Access-Control-Request-Headers: Seznam hlaviček požadavků, které aplikace nastavuje na skutečném požadavku. (Opět to nezahrnuje hlavičky, které prohlížeč nastavuje.)

Tady je příklad odpovědi za předpokladu, že server požadavek povoluje:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Length: 0
Access-Control-Allow-Origin: http://myclient.azurewebsites.net
Access-Control-Allow-Headers: x-my-custom-header
Access-Control-Allow-Methods: PUT
Date: Wed, 05 Jun 2013 06:33:22 GMT

Odpověď obsahuje hlavičku Access-Control-Allow-Methods, která obsahuje seznam povolených metod, a volitelně hlavičku Access-Control-Allow-Headers, která obsahuje seznam povolených hlaviček. Pokud je předběžná žádost úspěšná, prohlížeč odešle skutečný požadavek, jak je popsáno výše.

Nástroje běžně používané k testování koncových bodů s předběžnými požadavky OPTIONS (například Fiddler a Postman) ve výchozím nastavení neodesílají požadované hlavičky OPTIONS. Ověřte, že Access-Control-Request-Method se hlavičky a Access-Control-Request-Headers odesílají spolu s požadavkem a že se hlavičky OPTIONS dostanou k aplikaci prostřednictvím služby IIS.

Pokud chcete nakonfigurovat službu IIS tak, aby umožňovala aplikaci ASP.NET přijímat a zpracovávat požadavky OPTION, přidejte do souboruweb.config aplikace v <system.webServer><handlers> části následující konfiguraci:

<system.webServer>
  <handlers>
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
    <remove name="OPTIONSVerbHandler" />
    <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
  </handlers>
</system.webServer>

Odebrání brání službě OPTIONSVerbHandler IIS ve zpracování požadavků OPTIONS. Nahrazení funkce ExtensionlessUrlHandler-Integrated-4.0 umožňuje, aby se požadavky OPTIONS dostaly do aplikace, protože výchozí registrace modulu umožňuje pouze požadavky GET, HEAD, POST a DEBUG s adresami URL bez rozšíření.

Pravidla oboru pro [EnableCors]

Cors můžete povolit na akci, kontroler nebo globálně pro všechny řadiče webového rozhraní API ve vaší aplikaci.

Na akci

Chcete-li povolit CORS pro jednu akci, nastavte atribut [EnableCors] pro metodu action. Následující příklad povolí CORS pouze pro metodu GetItem .

public class ItemsController : ApiController
{
    public HttpResponseMessage GetAll() { ... }

    [EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
    public HttpResponseMessage GetItem(int id) { ... }

    public HttpResponseMessage Post() { ... }
    public HttpResponseMessage PutItem(int id) { ... }
}

Na kontroler

Pokud nastavíte [EnableCors] pro třídu kontroleru, platí pro všechny akce na kontroleru. Pokud chcete cors pro akci zakázat, přidejte do akce atribut [DisableCors]. Následující příklad povoluje CORS pro každou metodu s výjimkou PutItem.

[EnableCors(origins: "http://www.example.com", headers: "*", methods: "*")]
public class ItemsController : ApiController
{
    public HttpResponseMessage GetAll() { ... }
    public HttpResponseMessage GetItem(int id) { ... }
    public HttpResponseMessage Post() { ... }

    [DisableCors]
    public HttpResponseMessage PutItem(int id) { ... }
}

Globálně

Pokud chcete povolit CORS pro všechny kontrolery webového rozhraní API ve vaší aplikaci, předejte instanci EnableCorsAttribute metodě EnableCors :

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var cors = new EnableCorsAttribute("www.example.com", "*", "*");
        config.EnableCors(cors);
        // ...
    }
}

Pokud nastavíte atribut na více než jeden obor, pořadí priorit je následující:

  1. Akce
  2. Controller
  3. Globální

Nastavení povolených počátek

Parametr origins atributu [EnableCors] určuje, které zdroje mají povolený přístup k prostředku. Hodnota je čárkami oddělený seznam povolených počátek.

[EnableCors(origins: "http://www.contoso.com,http://www.example.com", 
    headers: "*", methods: "*")]

K povolení požadavků z libovolného původu můžete také použít hodnotu zástupných znaků "*".

Před povolením požadavků z jakéhokoli zdroje pečlivě zvažte. To znamená, že volání AJAX do vašeho webového rozhraní API může provádět doslova každý web.

// Allow CORS for all origins. (Caution!)
[EnableCors(origins: "*", headers: "*", methods: "*")]

Nastavení povolených metod HTTP

Parametr methods atributu [EnableCors] určuje, které metody HTTP mají povolený přístup k prostředku. Pokud chcete povolit všechny metody, použijte hodnotu zástupných znaků "*". Následující příklad umožňuje pouze požadavky GET a POST.

[EnableCors(origins: "http://www.example.com", headers: "*", methods: "get,post")]
public class TestController : ApiController
{
    public HttpResponseMessage Get() { ... }
    public HttpResponseMessage Post() { ... }
    public HttpResponseMessage Put() { ... }    
}

Nastavení povolených hlaviček požadavků

Tento článek dříve popsal, jak může předběžný požadavek obsahovat hlavičku Access-Control-Request-Headers se seznamem hlaviček HTTP nastavených aplikací (tzv. hlavičky žádostí o autory). Parametr headers atributu [EnableCors] určuje, které hlavičky požadavků autora jsou povoleny. Pokud chcete povolit libovolná záhlaví, nastavte záhlaví na *. Pokud chcete povolit konkrétní hlavičky, nastavte záhlaví na seznam povolených záhlaví oddělených čárkami:

[EnableCors(origins: "http://example.com", 
    headers: "accept,content-type,origin,x-my-header", methods: "*")]

Prohlížeče však nejsou zcela konzistentní v tom, jak nastavují hlavičky Access-Control-Request-Headers. Například Chrome v současné době obsahuje "origin". FireFox neobsahuje standardní hlavičky, jako je "Accept", a to ani v případě, že je aplikace nastaví ve skriptu.

Pokud nastavíte záhlaví na cokoli jiného než *, měli byste zahrnout alespoň "accept", "content-type" a "origin" plus všechny vlastní hlavičky, které chcete podporovat.

Nastavení povolených hlaviček odpovědí

Ve výchozím nastavení prohlížeč nezpřístupňuje aplikaci všechny hlavičky odpovědi. Ve výchozím nastavení jsou k dispozici hlavičky odpovědi:

  • Cache-Control
  • Jazyk obsahu
  • Typ obsahu
  • Platnost vyprší
  • Last-Modified
  • Pragma

Specifikace CORS volá tyto jednoduché hlavičky odpovědi. Pokud chcete aplikaci zpřístupnit další hlavičky, nastavte parametr exposedHeaders [EnableCors].

V následujícím příkladu metoda kontroleru Get nastaví vlastní hlavičku s názvem X-Custom-Header. Ve výchozím nastavení prohlížeč nezpřístupní tuto hlavičku v požadavku mezi zdroji. Pokud chcete hlavičku zpřístupnit, zahrňte do exponovaných hlaviček X-Custom-Header.

[EnableCors(origins: "*", headers: "*", methods: "*", exposedHeaders: "X-Custom-Header")]
public class TestController : ApiController
{
    public HttpResponseMessage Get()
    {
        var resp = new HttpResponseMessage()
        {
            Content = new StringContent("GET: Test message")
        };
        resp.Headers.Add("X-Custom-Header", "hello");
        return resp;
    }
}

Předávání přihlašovacích údajů v požadavcích mezi zdroji

Přihlašovací údaje vyžadují zvláštní zpracování v požadavku CORS. Ve výchozím nastavení prohlížeč neodesílá žádné přihlašovací údaje s požadavkem mezi zdroji. Přihlašovací údaje zahrnují soubory cookie i schémata ověřování HTTP. Pokud chcete odesílat přihlašovací údaje s požadavkem mezi zdroji, musí klient nastavit XMLHttpRequest.withCredentials na hodnotu true.

Přímé použití XMLHttpRequest :

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

V jQuery:

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

Kromě toho musí server povolit přihlašovací údaje. Pokud chcete ve webovém rozhraní API povolit přihlašovací údaje mezi zdroji, nastavte vlastnost SupportsCredentials na hodnotu true v atributu [EnableCors] :

[EnableCors(origins: "http://myclient.azurewebsites.net", headers: "*", 
    methods: "*", SupportsCredentials = true)]

Pokud je tato vlastnost true, odpověď HTTP bude obsahovat hlavičku Access-Control-Allow-Credentials. Tato hlavička informuje prohlížeč, že server povoluje přihlašovací údaje pro požadavek mezi zdroji.

Pokud prohlížeč odešle přihlašovací údaje, ale odpověď neobsahuje platnou hlavičku Access-Control-Allow-Credentials, prohlížeč nezpřístupní odpověď aplikaci a požadavek AJAX selže.

Buďte opatrní při nastavení SupportsCredentials na hodnotu true, protože to znamená, že web v jiné doméně může odesílat přihlašovací údaje přihlášeného uživatele do webového rozhraní API jménem uživatele, aniž by o tom uživatel věděl. Specifikace CORS také uvádí, že nastavení počátek na "*" je neplatné, pokud je hodnota SupportsCredentials pravdivá.

Vlastní poskytovatelé zásad CORS

Atribut [EnableCors] implementuje rozhraní ICorsPolicyProvider . Můžete poskytnout vlastní implementaci vytvořením třídy, která je odvozena z atributu a implementuje ICorsPolicyProvider.

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, AllowMultiple = false)]
public class MyCorsPolicyAttribute : Attribute, ICorsPolicyProvider 
{
    private CorsPolicy _policy;

    public MyCorsPolicyAttribute()
    {
        // Create a CORS policy.
        _policy = new CorsPolicy
        {
            AllowAnyMethod = true,
            AllowAnyHeader = true
        };

        // Add allowed origins.
        _policy.Origins.Add("http://myclient.azurewebsites.net");
        _policy.Origins.Add("http://www.contoso.com");
    }

    public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request)
    {
        return Task.FromResult(_policy);
    }
}

Teď můžete atribut použít na libovolné místo, kam byste umístili [EnableCors].

[MyCorsPolicy]
public class TestController : ApiController
{
    .. //

Například vlastní poskytovatel zásad CORS může číst nastavení z konfiguračního souboru.

Jako alternativu k použití atributů můžete zaregistrovat objekt ICorsPolicyProviderFactory , který vytváří objekty ICorsPolicyProvider .

public class CorsPolicyFactory : ICorsPolicyProviderFactory
{
    ICorsPolicyProvider _provider = new MyCorsPolicyProvider();

    public ICorsPolicyProvider GetCorsPolicyProvider(HttpRequestMessage request)
    {
        return _provider;
    }
}

Chcete-li nastavit ICorsPolicyProviderFactory, zavolejte metodu rozšíření SetCorsPolicyProviderFactory při spuštění následujícím způsobem:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        config.SetCorsPolicyProviderFactory(new CorsPolicyFactory());
        config.EnableCors();

        // ...
    }
}

Podpora prohlížečů

Balíček CORS webového rozhraní API je technologie na straně serveru. Prohlížeč uživatele musí také podporovat CORS. Aktuální verze všech hlavních prohlížečů naštěstí obsahují podporu CORS.