Access-control-allow-origin com múltiplos domínios

98

No meu web.config, gostaria de especificar mais de um domínio para a access-control-allow-origindiretiva. Eu não quero usar *. Eu tentei esta sintaxe:

<add name="Access-Control-Allow-Origin" value="http://localhost:1506, http://localhost:1502" />

este

<add name="Access-Control-Allow-Origin" value="http://localhost:1506 http://localhost:1502" />

este

<add name="Access-Control-Allow-Origin" value="http://localhost:1506; http://localhost:1502" />

e este

<add name="Access-Control-Allow-Origin" value="http://localhost:1506" />
<add name="Access-Control-Allow-Origin" value="http://localhost:1502" />

mas nenhum deles funciona. Qual é a sintaxe correta ?

Sam
fonte

Respostas:

78

Só pode haver um Access-Control-Allow-Origincabeçalho de resposta e esse cabeçalho só pode ter um valor de origem. Portanto, para fazer isso funcionar, você precisa ter algum código que:

  1. Pega o Origincabeçalho da solicitação.
  2. Verifica se o valor de origem é um dos valores da lista de desbloqueio.
  3. Se for válido, define o Access-Control-Allow-Origincabeçalho com esse valor.

Não acho que haja alguma maneira de fazer isso apenas por meio do web.config.

if (ValidateRequest()) {
    Response.Headers.Remove("Access-Control-Allow-Origin");
    Response.AddHeader("Access-Control-Allow-Origin", Request.UrlReferrer.GetLeftPart(UriPartial.Authority));

    Response.Headers.Remove("Access-Control-Allow-Credentials");
    Response.AddHeader("Access-Control-Allow-Credentials", "true");

    Response.Headers.Remove("Access-Control-Allow-Methods");
    Response.AddHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
}
monsur
fonte
2
Isso responde minha pergunta. Não sei por que a Microsoft não permite a especificação de origens múltiplas no web.config ...
Sam
17
Onde posso adicionar este código? Tenho arquivos de texto simples gerados pelo servidor e lidos via AJAX, sem nenhum código. Onde posso colocar o código para restringir o acesso a arquivos de texto em meu diretório?
Harry
3
@Simon_Weaver existe um *valor que permite que qualquer origem acesse o recurso. No entanto, a pergunta original era sobre como colocar na lista de permissões um conjunto de domínios.
Monsur
2
como sou novo no asp .net, posso perguntar onde posso colocar esse código no meu projeto de API da web asp .net?
Amrit
92

Para IIS 7.5+ e Rewrite 2.0, você pode usar:

<system.webServer>
   <httpProtocol>
     <customHeaders>
         <add name="Access-Control-Allow-Headers" value="Origin, X-Requested-With, Content-Type, Accept" />
         <add name="Access-Control-Allow-Methods" value="POST,GET,OPTIONS,PUT,DELETE" />
     </customHeaders>
   </httpProtocol>
        <rewrite>            
            <outboundRules>
                <clear />                
                <rule name="AddCrossDomainHeader">
                    <match serverVariable="RESPONSE_Access_Control_Allow_Origin" pattern=".*" />
                    <conditions logicalGrouping="MatchAll" trackAllCaptures="true">
                        <add input="{HTTP_ORIGIN}" pattern="(http(s)?://((.+\.)?domain1\.com|(.+\.)?domain2\.com|(.+\.)?domain3\.com))" />
                    </conditions>
                    <action type="Rewrite" value="{C:0}" />
                </rule>           
            </outboundRules>
        </rewrite>
 </system.webServer>

Explicando a RESPONSE_Access_Control_Allow_Originparte da variável do servidor :
Em Rewrite, você pode usar qualquer string após RESPONSE_e criará o Cabeçalho de Resposta usando o resto da palavra como o nome do cabeçalho (neste caso Access-Control-Allow-Origin). Rewrite usa sublinhados "_" em vez de travessões "-" (reescrever os converte em travessões)

Explicando a variável do servidor HTTP_ORIGIN:
Da mesma forma, em Rewrite você pode pegar qualquer cabeçalho de solicitação usando HTTP_como prefixo. Mesmas regras com os travessões (use sublinhados "_" em vez de travessões "-").

Paco Zarate
fonte
Você consegue pensar em algum motivo pelo qual isso não funcionaria com o IIS 7.5?
Phil Ricketts
Eu acho que isso deve resultar. Especifiquei a versão do IIS 8.5 porque foi onde a testei.
Paco Zarate
4
@PacoZarate Boa, ótima dica. Para simplificar para regex e torná-lo mais genérico, você pode usar - (http(s)?:\/\/((.+\.)?(domain1|domain2)\.(com|org|net))). Dessa forma, você pode adicionar outros domínios com bastante facilidade e oferecer suporte a vários domínios de nível superior (por exemplo, com, org, net etc.).
Merlin
4
Tentei isso no IIS 7.5. Parece estar funcionando bem.
Prescient
2
Está tendo problemas com o cache? Depois de ajustar o web.config, o primeiro site que eu acesso corresponde bem, mas o segundo retorna o mesmo cabeçalho do primeiro. Fazendo assim com que os domínios não correspondam muito.
Airn5475
20

Na Web.API, este atributo pode ser adicionado usando Microsoft.AspNet.WebApi.Corsconforme detalhado em http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

No MVC, você pode criar um atributo de filtro para fazer este trabalho para você:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,
                AllowMultiple = true, Inherited = true)]
public class EnableCorsAttribute : FilterAttribute, IActionFilter {
    private const string IncomingOriginHeader = "Origin";
    private const string OutgoingOriginHeader = "Access-Control-Allow-Origin";
    private const string OutgoingMethodsHeader = "Access-Control-Allow-Methods";
    private const string OutgoingAgeHeader = "Access-Control-Max-Age";

    public void OnActionExecuted(ActionExecutedContext filterContext) {
        // Do nothing
    }

    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        var isLocal = filterContext.HttpContext.Request.IsLocal;
        var originHeader = 
             filterContext.HttpContext.Request.Headers.Get(IncomingOriginHeader);
        var response = filterContext.HttpContext.Response;

        if (!String.IsNullOrWhiteSpace(originHeader) &&
            (isLocal || IsAllowedOrigin(originHeader))) {
            response.AddHeader(OutgoingOriginHeader, originHeader);
            response.AddHeader(OutgoingMethodsHeader, "GET,POST,OPTIONS");
            response.AddHeader(OutgoingAgeHeader, "3600");
        }
    }

    protected bool IsAllowedOrigin(string origin) {
        // ** replace with your own logic to check the origin header
        return true;
    }
}

Em seguida, habilite-o para ações / controladores específicos:

[EnableCors]
public class SecurityController : Controller {
    // *snip*
    [EnableCors]
    public ActionResult SignIn(Guid key, string email, string password) {

Ou adicione-o para todos os controladores em Global.asax.cs

protected void Application_Start() {
    // *Snip* any existing code

    // Register global filter
    GlobalFilters.Filters.Add(new EnableCorsAttribute());
    RegisterGlobalFilters(GlobalFilters.Filters);

    // *snip* existing code
}
Rob Church
fonte
Você sabe para quais versões do .Net / MVC isso funciona?
Keab42 de
Estou usando isso com sucesso em .net 4 / MVC 3 - pelo que sei, deve funcionar em versões superiores, mas pode haver uma maneira preferida de registrar o filtro global em versões MVC posteriores.
Rob Church
apenas observe sua solução WEB API 2 apenas. não para WEB API 1.
Samih A
5

Depois de ler todas as respostas e experimentá-las, nenhum deles me ajudou. O que eu descobri ao pesquisar em outro lugar é que você pode criar um atributo personalizado que você pode adicionar ao seu controlador. Ele sobrescreve os do EnableCors e adiciona os domínios da lista de permissões nele.

Esta solução está funcionando bem porque permite que você tenha os domínios permitidos no webconfig (appsettings) em vez de codificá-los no atributo EnableCors no seu controlador.

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
public class EnableCorsByAppSettingAttribute : Attribute, ICorsPolicyProvider
{
    const string defaultKey = "whiteListDomainCors";
    private readonly string rawOrigins;
    private CorsPolicy corsPolicy;

    /// <summary>
    /// By default uses "cors:AllowedOrigins" AppSetting key
    /// </summary>
    public EnableCorsByAppSettingAttribute()
        : this(defaultKey) // Use default AppSetting key
    {
    }

    /// <summary>
    /// Enables Cross Origin
    /// </summary>
    /// <param name="appSettingKey">AppSetting key that defines valid origins</param>
    public EnableCorsByAppSettingAttribute(string appSettingKey)
    {
        // Collect comma separated origins
        this.rawOrigins = AppSettings.whiteListDomainCors;
        this.BuildCorsPolicy();
    }

    /// <summary>
    /// Build Cors policy
    /// </summary>
    private void BuildCorsPolicy()
    {
        bool allowAnyHeader = String.IsNullOrEmpty(this.Headers) || this.Headers == "*";
        bool allowAnyMethod = String.IsNullOrEmpty(this.Methods) || this.Methods == "*";

        this.corsPolicy = new CorsPolicy
        {
            AllowAnyHeader = allowAnyHeader,
            AllowAnyMethod = allowAnyMethod,
        };

        // Add origins from app setting value
        this.corsPolicy.Origins.AddCommaSeperatedValues(this.rawOrigins);
        this.corsPolicy.Headers.AddCommaSeperatedValues(this.Headers);
        this.corsPolicy.Methods.AddCommaSeperatedValues(this.Methods);
    }

    public string Headers { get; set; }
    public string Methods { get; set; }

    public Task<CorsPolicy> GetCorsPolicyAsync(HttpRequestMessage request,
                                               CancellationToken cancellationToken)
    {
        return Task.FromResult(this.corsPolicy);
    }
}

    internal static class CollectionExtensions
{
    public static void AddCommaSeperatedValues(this ICollection<string> current, string raw)
    {
        if (current == null)
        {
            return;
        }

        var paths = new List<string>(AppSettings.whiteListDomainCors.Split(new char[] { ',' }));
        foreach (var value in paths)
        {
            current.Add(value);
        }
    }
}

Encontrei este guia online e funcionou perfeitamente:

http://jnye.co/Posts/2032/dynamic-cors-origins-from-appsettings-using-web-api-2-2-cross-origin-support

Pensei em deixar isso aqui para quem precisa.

Helpha
fonte
Esta é uma resposta apenas com link. Em vez disso, faça com que a resposta seja independente.
Reintegrar Monica em
1
Ok, eu sou novo aqui, isso é mais parecido com o que deveria ser ??
Helpha
3

Consegui resolver isso no código de tratamento de solicitação seguindo o conselho de 'monsur'.

string origin = WebOperationContext.Current.IncomingRequest.Headers.Get("Origin");

WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", origin);
bsandhu
fonte
Essa é a maneira de fazer em webform, por exemplo. Basta usar Request.Headers quando disponível. E, se necessário, use uma lista de permissões para filtrar apenas os domínios permitidos.
AFract
3
Isso é tão bom quanto adicionar <add name = "Access-Control-Allow-Origin" value = "*" /> no arquivo web.config
Isaiah4110
3

Para IIS 7.5+, você pode usar o módulo IIS CORS: https://www.iis.net/downloads/microsoft/iis-cors-module

Seu web.config deve ser algo assim:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <system.webServer>
        <cors enabled="true" failUnlistedOrigins="true">
            <add origin="http://localhost:1506">
                <allowMethods>                    
                    <add method="GET" />
                    <add method="HEAD" />
                    <add method="POST" />
                    <add method="PUT" /> 
                    <add method="DELETE" /> 
                </allowMethods>
            </add>
            <add origin="http://localhost:1502">
                <allowMethods>
                    <add method="GET" />
                    <add method="HEAD" />
                    <add method="POST" />
                    <add method="PUT" /> 
                    <add method="DELETE" /> 
                </allowMethods>
            </add>
        </cors>
    </system.webServer>
</configuration>

Você pode encontrar a referência de configuração aqui: https://docs.microsoft.com/en-us/iis/extensions/cors-module/cors-module-configuration-reference

Mario arturo
fonte
Se isso funcionar como diz, gostaria que você tivesse postado 3 anos atrás! Uau!
Michael
1

Você pode adicionar este código ao seu projeto webapi asp.net

no arquivo Global.asax

    protected void Application_BeginRequest()
{
    string origin = Request.Headers.Get("Origin");
    if (Request.HttpMethod == "OPTIONS")
    {
        Response.AddHeader("Access-Control-Allow-Origin", origin);
        Response.AddHeader("Access-Control-Allow-Headers", "*");
        Response.AddHeader("Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS,DELETE");
        Response.StatusCode = 200;
        Response.End();
    }
    else
    {
        Response.AddHeader("Access-Control-Allow-Origin", origin);
        Response.AddHeader("Access-Control-Allow-Headers", "*");
        Response.AddHeader("Access-Control-Allow-Methods", "GET,POST,PUT,OPTIONS,DELETE");
    }
}
Jackdon Wang
fonte
0

Você pode usar o middleware owin para definir a política de cors na qual você pode definir várias origens de cors

return new CorsOptions
        {
            PolicyProvider = new CorsPolicyProvider
            {
                PolicyResolver = context =>
                {
                    var policy = new CorsPolicy()
                    {
                        AllowAnyOrigin = false,
                        AllowAnyMethod = true,
                        AllowAnyHeader = true,
                        SupportsCredentials = true
                    };
                    policy.Origins.Add("http://foo.com");
                    policy.Origins.Add("http://bar.com");
                    return Task.FromResult(policy);
                }
            }
        };
Chayan Banerjee
fonte
-3

Você só precisa:

  • adicione um Global.asax ao seu projeto,
  • exclua <add name="Access-Control-Allow-Origin" value="*" />do seu web.config.
  • depois, adicione isso no Application_BeginRequestmétodo de Global.asax:

    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin","*");
    
    if (HttpContext.Current.Request.HttpMethod == "OPTIONS")
    {
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Methods", "POST,GET,OPTIONS,PUT,DELETE");
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept");
        HttpContext.Current.Response.End();
    }

Espero esta ajuda. que funciona para mim.

joanrm20
fonte
Adicionar "...- Origem: *" funciona, exceto quando você permite credenciais. Se você tiver allow-credentials definido como true, deverá especificar um domínio (não simplesmente *). É aí que reside o ponto crucial do problema. Caso contrário, você poderia apenas especificar "... allow-credentials: false" e pronto.
Richard