Como funciona o CORS

Esta seção descreve o que acontece em uma solicitação CORS no nível das mensagens HTTP. É importante entender como funciona o CORS, para que você possa configurar o [EnableCors] atributo corretamente e solucionar problemas se as coisas não funcionam conforme o esperado.

A especificação CORS apresenta vários novos cabeçalhos HTTP que permitem que solicitações entre origens. Se um navegador oferece suporte a CORS, ele define esses cabeçalhos automaticamente para solicitações entre origens; Você não precisa fazer nada especial em seu código JavaScript.

Aqui está um exemplo de uma solicitação entre origens. O cabeçalho de “Origem” fornece o domínio do site que está fazendo a solicitação.consoleCopiar

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

Se o servidor permite que a solicitação, ele define o cabeçalho Access-Control-Allow-Origin. O valor desse cabeçalho corresponde o cabeçalho de origem, ou é o valor de curinga “*”, o que significa que qualquer origem é permitida.consoleCopiar

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

Se a resposta não incluir o cabeçalho Access-Control-Allow-Origin, a solicitação AJAX falha. Especificamente, o navegador não permite a solicitação. Mesmo se o servidor retorna uma resposta bem-sucedida, o navegador não disponibiliza a resposta para o aplicativo cliente.

Solicitações de simulação

Para algumas solicitações CORS, o navegador envia uma solicitação adicional, chamada de uma “solicitação de simulação,” antes de enviar a solicitação real para o recurso.

O navegador pode ignorar a solicitação de simulação se as seguintes condições forem verdadeiras:

  • O método de solicitação é GET, HEAD ou POST, e
  • O aplicativo não definir quaisquer cabeçalhos de solicitação que não seja Accept, Accept-Language, Content-Language, Content-Type ou última–ID do evento, e
  • O cabeçalho Content-Type (se definido) é um dos seguintes:
    • application/x-www-form-urlencoded
    • multipart/form-data
    • texto/simples

A regra sobre cabeçalhos de solicitação se aplica aos cabeçalhos que o aplicativo define chamando setRequestHeader sobre o XMLHttpRequest objeto. (A especificação CORS chama esses cabeçalhos de solicitação”autor”.) A regra não se aplica aos cabeçalhos de navegador pode definir, como o agente do usuário, o Host ou o comprimento do conteúdo.

Aqui está um exemplo de uma solicitação de simulação:consoleCopiar

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

A solicitação de simulação usa o método HTTP OPTIONS. Ele inclui dois cabeçalhos especiais:

  • Access-Control-Request-Method: O método HTTP que será usado para a solicitação real.
  • Access-Control-Request-Headers: Uma lista de cabeçalhos de solicitação que o aplicativo configurado na solicitação real. (Novamente, isso não inclui cabeçalhos que define o navegador.)

Aqui está um exemplo de resposta, supondo que o servidor permite que a solicitação:consoleCopiar

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

A resposta inclui um cabeçalho Access-Control-Allow-Methods que lista os métodos permitidos e, opcionalmente, um cabeçalho Access-Control-Allow-Headers, que lista os cabeçalhos permitidos. Se a solicitação de simulação for bem-sucedida, o navegador envia a solicitação real, conforme descrito anteriormente.

Ferramentas usadas comumente para testar os pontos de extremidade com solicitações de simulação OPTIONS (por exemplo, Fiddler e Postman) não enviam os cabeçalhos necessários de opções por padrão. Confirme se o Access-Control-Request-Method e Access-Control-Request-Headers cabeçalhos são enviados com a solicitação e cabeçalhos de opções cheguem ao aplicativo por meio do IIS.

Para configurar o IIS para permitir que um aplicativo ASP.NET receber e lidar com a opção as solicitações, adicione a seguinte configuração para o aplicativo Web. config arquivo no <system.webServer><handlers> seção:XMLCopiar

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

A remoção de OPTIONSVerbHandler impede que o IIS tratar as solicitações de opções. A substituição de ExtensionlessUrlHandler-Integrated-4.0 permite opções solicitações cheguem ao aplicativo, porque o registro do módulo padrão permite somente solicitações GET, HEAD, POST e depuração com URLs sem extensão.

Regras de escopo para [EnableCors]

Você pode habilitar o CORS por ação, por controlador ou globalmente para todos os controladores de API da Web em seu aplicativo.

Por ação

Para habilitar o CORS para uma única ação, defina as [EnableCors] atributo no método de ação. O exemplo a seguir habilita o CORS para o GetItem somente no método.C#Copiar

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

Por controlador

Se você definir [EnableCors] na classe do controlador, ele se aplica a todas as ações no controlador. Para desabilitar CORS para uma ação, adicione a [DisableCors] atributo à ação. O exemplo a seguir habilita o CORS para cada método, exceto PutItem.C#Copiar

[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) { ... }
}

Globalmente

Para habilitar o CORS para todos os controladores de API da Web em seu aplicativo, passe uma EnableCorsAttribute da instância para o EnableCors método:C#Copiar

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

Se você definir o atributo em mais de um escopo, a ordem de precedência é:

  1. Ação
  2. Controlador
  3. Global

Defina as origens permitidas

origens parâmetro do [EnableCors] atributo especifica quais origens têm permissão para acessar o recurso. O valor é uma lista separada por vírgulas das origens permitidas.C#Copiar

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

Você também pode usar o valor de curinga “*” para permitir solicitações de qualquer origem.

Considere cuidadosamente antes de permitir que solicitações de qualquer origem. Isso significa que, literalmente, qualquer site possa fazer chamadas AJAX para sua API da web.C#Copiar

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

Defina os métodos HTTP permitidos

métodos parâmetro do [EnableCors] atributo especifica quais métodos HTTP têm permissão para acessar o recurso. Para permitir que todos os métodos, use o valor de curinga “*”. O exemplo a seguir permite que somente as solicitações GET e POST.C#Copiar

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

Definir os cabeçalhos de solicitação permitido

Este artigo descreveu anteriormente como uma solicitação de simulação pode incluir um cabeçalho Access-Control-Request-Headers, listando os cabeçalhos HTTP definidos pelo aplicativo (os chamados “author cabeçalhos de solicitação”). O cabeçalhos parâmetro do [EnableCors] atributo especifica quais cabeçalhos de solicitação do autor são permitidos. Para permitir que todos os cabeçalhos, defina cabeçalhos para “*”. Para cabeçalhos específicos de lista de permissões, defina cabeçalhos a uma lista separada por vírgula de cabeçalhos permitidos:C#Copiar

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

No entanto, os navegadores não são totalmente consistentes em como eles definir Access-Control-Request-Headers. Por exemplo, o Chrome atualmente inclui “origem”. FireFox não inclui cabeçalhos padrão, como “Aceitar”, mesmo quando o aplicativo define-los no script.

Se você definir cabeçalhos para qualquer coisa diferente de “*”, você deve incluir pelo menos “accept”, “content-type” e “origem”, além de quaisquer cabeçalhos personalizados que você deseja dar suporte.

Definir os cabeçalhos de resposta permitidos

Por padrão, o navegador não expõe todos os cabeçalhos de resposta para o aplicativo. Os cabeçalhos de resposta que estão disponíveis por padrão são:

  • Cache-Control
  • Idioma do conteúdo
  • Tipo de conteúdo
  • Expira
  • Última modificação
  • Pragma

A especificação CORS chama esses cabeçalhos de resposta simples. Para disponibilizar outros cabeçalhos para o aplicativo, defina as exposedHeaders parâmetro do [EnableCors].

No exemplo a seguir, o controlador Get método define um cabeçalho personalizado chamado ‘X-Custom-Header’. Por padrão, o navegador não irá expor esse cabeçalho em uma solicitação entre origens. Para disponibilizar o cabeçalho, incluir ‘X-Custom-Header’ em exposedHeaders.C#Copiar

[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;
    }
}

Passar credenciais nas solicitações entre origens

Credenciais exigem tratamento especial em uma solicitação CORS. Por padrão, o navegador não envia todas as credenciais com uma solicitação entre origens. As credenciais incluem cookies, bem como os esquemas de autenticação HTTP. Para enviar as credenciais com uma solicitação entre origens, o cliente deve definir XMLHttpRequest.withCredentials como true.

Usando o XMLHttpRequest diretamente:C#Copiar

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

No jQuery:JavaScriptCopiar

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

Além disso, o servidor deve permitir que as credenciais. Para permitir credenciais entre origens na API da Web, defina as SupportsCredentials propriedade como true na [EnableCors] atributo:C#Copiar

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

Se essa propriedade for true, a resposta HTTP incluirá um cabeçalho Access-Control-Allow-Credentials. Esse cabeçalho informa ao navegador que o servidor permite que as credenciais para uma solicitação entre origens.

Se o navegador envia as credenciais, mas a resposta não inclui um cabeçalho Access-Control-Allow-Credentials válido, o navegador não exporá a resposta para o aplicativo e a solicitação AJAX falhar.

Tenha cuidado sobre a configuração SupportsCredentials como true, porque significa que um site em outro domínio pode enviar credenciais do usuário conectado à sua API da Web em nome do usuário, sem que o usuário estar ciente. A especificação CORS também declara que a configuração origens à ” * ” não é válido se SupportsCredentials é verdadeiro.

Provedores de política CORS personalizados

[EnableCors] atributo implementa o ICorsPolicyProvider interface. Você pode fornecer sua própria implementação, criando uma classe que deriva de atributo e implementa ICorsPolicyProvider.C#Copiar

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

Agora você pode aplicar o atributo de qualquer lugar que você colocaria [EnableCors].C#Copiar

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

Por exemplo, um provedor de política CORS personalizado pode ler as configurações de um arquivo de configuração.

Como uma alternativa ao uso de atributos, você pode registrar um ICorsPolicyProviderFactory objeto cria ICorsPolicyProvider objetos.C#Copiar

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

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

Para definir a ICorsPolicyProviderFactory, chame o SetCorsPolicyProviderFactory método de extensão na inicialização, da seguinte maneira:C#Copiar

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

        // ...
    }
}

Suporte ao navegador

O pacote de CORS da API Web é uma tecnologia do lado do servidor. O navegador do usuário também precisa oferecer suporte a CORS. Felizmente, as versões atuais de todos os principais navegadores incluem suporte para CORS.

Fonte: https://docs.microsoft.com/pt-br/aspnet/web-api/overview/security/enabling-cross-origin-requests-in-web-api

Deixe uma resposta