Sdílet prostřednictvím


Povolení požadavků mezi zdroji ve webovém rozhraní API 2 ASP.NET

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 žádostí mezi zdroji (CORS) v ASP.NET Core najdete v tématech:

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ásada 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 nechat jiné weby volat webové rozhraní API.

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

Software použitý v tomto kurzu

Úvod

Tento kurz ukazuje podporu CORS ve webovém rozhraní API ASP.NET. Začneme vytvořením dvou ASP.NET projektů – jednoho s názvem WebService, který hostuje kontroler webového rozhraní API, a druhý s názvem "WebClient", který volá webovou službu. Vzhledem k tomu, že tyto dvě aplikace jsou hostované v různých doménách, požadavek AJAX z webového klienta na webovou službu je požadavek mezi zdroji.

Zobrazuje webové služby a webového klienta.

Co je "stejný původ"?

Dva adresy URL mají stejný původ, pokud mají stejná 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í dva:

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

Poznámka:

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

Vytvoření projektu webové služby

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 téma Začínáme s webovým rozhraním API ASP.NET.

  1. Spusťte Visual Studio a vytvořte nový projekt webové aplikace ASP.NET (.NET Framework).

  2. V dialogovém okně Nová webová aplikace ASP.NET vyberte prázdnou šablonu projektu. V části Přidat složky a základní odkazy 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ázvem TestController s následujícím 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 nasadit do Azure. (Pro snímky obrazovky v tomto kurzu se aplikace nasadí do Aplikace Azure Service Web Apps.) Pokud chcete ověřit, že webové rozhraní API funguje, přejděte do http://hostname/api/test/umístění , kde název hostitele je doména, do které jste nasadili aplikaci. 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 webové aplikace (.NET Framework) a vyberte šablonu projektu MVC . Volitelně vyberte 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ý projekt ASP.NET 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ý web.

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). Díky tomu můžete zkoumat různé požadavky mezi zdroji. V současné době aplikace WebService nepodporuje CORS, takže pokud kliknete na tlačítko, zobrazí se chyba.

Chyba Vyzkoušet v prohlížeči

Poznámka:

Pokud sledujete provoz HTTP v nástroji, jako je Fiddler, uvidíte, že prohlížeč odešle požadavek GET 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 nezabrání prohlížeči v odesílání požadavku. Místo toho zabrání aplikaci zobrazit odpověď.

Webový ladicí program Fiddler zobrazující webové požadavky

Povolení CORS

Teď povolíme CORS v aplikaci WebService. Nejprve přidejte balíček CORS NuGet. V sadě Visual Studio v nabídce Nástroje vyberte Správce balíčků NuGet a pak vyberte Správce balíčků Konzola. V okně konzoly 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 můžete cílit 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 přidejte atribut [EnableCors] do TestController třídy:

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...
    }
}

Pro parametr origins použijte identifikátor URI, do kterého jste nasadili aplikaci WebClient. To umožňuje požadavky mezi zdroji z Webového klienta, ale přesto nepovolují všechny ostatní požadavky napříč doménou. Později podrobněji popíšem parametry pro [EnableCors].

Nezahrnujte lomítko na konec počáteční adresy URL.

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

Webový prohlížeč zobrazující úspěšnou testovací zprávu

Jak CORS funguje

Tato část popisuje, co se stane v požadavku CORS na úrovni zpráv HTTP. Je důležité pochopit, jak CORS funguje, abyste mohli správně nakonfigurovat atribut [EnableCors] a řešit potíže, pokud věci nefungují 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í tato záhlaví automaticky pro žádosti 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 dává doméně lokality, 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 je hodnota zástupného znaku "*", což znamená, že jakýkoli zdroj 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 zakáže. I když server vrátí úspěšnou odpověď, prohlížeč nepřístupní odpověď klientské aplikaci.

Předběžné požadavky

U některých požadavků CORS odešle prohlížeč další požadavek, kterému se říká "předběžný požadavek", před odesláním skutečného požadavku na prostředek.

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é hlavičky požadavků jiné 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/plain

Pravidlo týkající se hlaviček požadavku se vztahuje na hlavičky, které aplikace nastaví voláním setRequestHeader v objektu XMLHttpRequest . (Specifikace CORS volá tato "hlavičky požadavku autora".) Pravidlo se nevztahuje na hlavičky , které prohlížeč můž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ředběžný požadavek používá metodu HTTP OPTIONS. Obsahuje dvě speciální hlavičky:

  • Access-Control-Request-Method: Metoda HTTP, která se použije pro skutečný požadavek.
  • Hlavičky žádosti řízení přístupu: Seznam hlaviček požadavků, které aplikace nastavil na skutečném požadavku. (Opět to neobsahuje hlavičky, které prohlížeč nastaví.)

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 předběžný požadavek proběhne ú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 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 hlavičky odesílají s požadavkem a že se hlavičky OPTIONS dostanou do aplikace 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 souboru web.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 v zpracování požadavků OPTIONS. ExtensionlessUrlHandler-Integrated-4.0 Nahrazení 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, na kontroler nebo globálně pro všechny kontrolery webového rozhraní API ve vaší aplikaci.

Na akci

Chcete-li povolit CORS pro jednu akci, nastavte atribut [EnableCors] pro metodu akce. Následující příklad povoluje 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 pro třídu kontroleru nastavíte [EnableCors], 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 umožňuje 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 cors povolit 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 atribut nastavíte na více než jeden obor, pořadí priorit je:

  1. Akce
  2. Ovladač
  3. Globální

Nastavení povolených zdrojů

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

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

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

Před povolením požadavků z jakéhokoli zdroje pečlivě zvažte. Znamená to, že doslova každý web může volat AJAX do webového rozhraní API.

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

Nastavení povolených metod HTTP

Parametr metody atributu [EnableCors] určuje, které metody HTTP mají povolený přístup k prostředku. Pokud chcete povolit všechny metody, použijte hodnotu se zástupným znakem "*". 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í hlaviček povolených požadavků

Tento článek popisuje, jak předběžný požadavek může obsahovat hlavičku hlavičky access-Control-Request-Headers, která uvádí hlavičky HTTP nastavené aplikací (tzv. hlavičky požadavku autora). Parametr headers atributu [EnableCors] určuje, které hlavičky požadavku autora jsou povoleny. Pokud chcete povolit všechna záhlaví, nastavte záhlaví na "*". Pokud chcete povolit konkrétní záhlaví, nastavte záhlaví na čárkami oddělený seznam povolených hlaviček:

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

Prohlížeče ale nejsou zcela konzistentní v tom, jak nastavují hlavičky access-Control-Request-Headers. Chrome například aktuálně obsahuje "origin". FireFox neobsahuje standardní hlavičky, jako je "Accept", i když je aplikace nastaví ve skriptu.

Pokud nastavíte hlavičky na cokoli jiného než "*", měli byste zahrnout aspoň "accept", "content-type" a "origin" a 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. Hlavičky odpovědi, které jsou ve výchozím nastavení k dispozici, jsou:

  • Řízení mezipaměti
  • Jazyk obsahu
  • Typ obsahu
  • Vyprší
  • Naposledy změněno
  • Pragma

Specifikace CORS volá tato jednoduchá hlavička odpovědi. Pokud chcete zpřístupnit další hlavičky pro aplikaci, nastavte parametr exposedHeaders funkce [EnableCors].< a1/>.

V následujícím příkladu nastaví metoda kontroleru Get 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 záhlaví zpřístupnit, zahrňte do exposedHeaders 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ání přihlašovacích údajů v požadavcích mezi zdroji

Přihlašovací údaje vyžadují speciální 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.

Použití xmlHttpRequest přímo:

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 u atributu [EnableCors] :

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

Pokud je tato vlastnost pravdivá, odpověď HTTP bude obsahovat hlavičku Access-Control-Allow-Credentials. Tato hlavička říká prohlížeči, ž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č nezobrazí odpověď aplikaci a požadavek AJAX selže.

Dávejte pozor na nastavení SupportsCredentials na hodnotu true, protože to znamená, že web v jiné doméně může odeslat přihlašovací údaje přihlášeného uživatele do vašeho webového rozhraní API jménem uživatele bez toho, aby ho uživatel věděl. Specifikace CORS také uvádí, že nastavení původu na "*" je neplatné, pokud je hodnota SupportsCredentials pravdivá.

Vlastní poskytovatelé zásad CORS

Atribut [EnableCors] implementuje ICorsPolicyProvider rozhraní. Vlastní implementaci můžete poskytnout vytvořením třídy, která je odvozena z Attribute 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 místě, kam byste umístili [EnableCors].< a1/>.

[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 ICorsPolicyProviderFactory objekt, který vytváří ICorsPolicyProvider objekty.

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

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

Chcete-li nastavit ICorsPolicyProviderFactory, volání SetCorsPolicyProviderFactory rozšiřující metoda 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í zahrnují podporu CORS.