Access-control-allow-origin avec plusieurs domaines

99

Dans mon web.config, je voudrais spécifier plus d'un domaine pour la access-control-allow-origindirective. Je ne veux pas utiliser *. J'ai essayé cette syntaxe:

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

celui-là

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

celui-là

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

et celui-là

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

mais aucun d'eux ne fonctionne. Quelle est la syntaxe correcte?

Sam
la source

Réponses:

79

Il ne peut y avoir qu'un seul en- Access-Control-Allow-Origintête de réponse et cet en-tête ne peut avoir qu'une seule valeur d'origine. Par conséquent, pour que cela fonctionne, vous devez avoir un code qui:

  1. Récupère l'en- Origintête de la demande.
  2. Vérifie si la valeur d'origine est l'une des valeurs de la liste blanche.
  3. S'il est valide, définit l'en- Access-Control-Allow-Origintête avec cette valeur.

Je ne pense pas qu'il existe un moyen de le faire uniquement via le 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
la source
2
Cela répond à ma question. Je ne sais pas pourquoi Microsoft n'autorise pas la spécification de plusieurs origines dans le web.config ...
Sam
17
Où puis-je ajouter ce code? J'ai des fichiers texte générés par le serveur et lus via AJAX, pas de code du tout. Où puis-je mettre le code pour restreindre l'accès aux fichiers texte de mon répertoire?
Harry
3
@Simon_Weaver il existe une *valeur qui permet à n'importe quelle origine d'accéder à la ressource. Cependant, la question d'origine portait sur la mise en liste blanche d'un ensemble de domaines.
monsur
2
comme je suis nouveau sur asp .net, puis-je demander où puis-je mettre ce code dans mon projet d'api web asp .net?
Amrit
93

Pour IIS 7.5+ et Rewrite 2.0, vous pouvez utiliser:

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

Explication de la RESPONSE_Access_Control_Allow_Originpartie variable du serveur :
Dans Rewrite, vous pouvez utiliser n'importe quelle chaîne aprèsRESPONSE_ et cela créera l'en-tête de réponse en utilisant le reste du mot comme nom d'en-tête (dans ce cas Access-Control-Allow-Origin). La réécriture utilise des traits de soulignement "_" au lieu des tirets "-" (la réécriture les convertit en tirets)

Explication de la variable serveur HTTP_ORIGIN:
De même, dans Rewrite, vous pouvez récupérer n'importe quel en-tête de demande en utilisant HTTP_comme préfixe. Mêmes règles avec les tirets (utilisez des traits de soulignement "_" au lieu des tirets "-").

Paco Zarate
la source
Pouvez-vous penser à des raisons pour lesquelles cela ne fonctionnerait pas avec IIS 7.5?
Phil Ricketts
Je pense que ça devrait marcher. J'ai spécifié la version IIS 8.5 car c'est là que je l'ai testée.
Paco Zarate
4
@PacoZarate Belle, bonne astuce. Pour simplifier à regex et le rendre plus générique, vous pouvez utiliser - (http(s)?:\/\/((.+\.)?(domain1|domain2)\.(com|org|net))). De cette façon, vous pouvez ajouter d'autres domaines assez facilement et prendre en charge plusieurs domaines de premier niveau (par exemple com, org, net, etc.).
Merlin
4
Je viens d'essayer cela dans IIS 7.5. Semble fonctionner très bien.
Prescient
2
Vous rencontrez des problèmes avec la mise en cache? Après avoir peaufiné le web.config, le premier site Web sur lequel je vais correspond bien, mais le second renvoie le même en-tête que le premier. Ainsi, les domaines ne correspondent pas trop.
Airn5475
20

Dans Web.API, cet attribut peut être ajouté en utilisant Microsoft.AspNet.WebApi.Corscomme détaillé à http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api

Dans MVC, vous pouvez créer un attribut de filtre pour effectuer ce travail à votre place:

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

Ensuite, activez-le pour des actions / contrôleurs spécifiques:

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

Ou ajoutez-le pour tous les contrôleurs dans 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
la source
Savez-vous pour quelles versions de .Net / MVC cela fonctionne?
Keab42 du
J'utilise cela avec succès dans .net 4 / MVC 3 - pour autant que je sache, cela devrait fonctionner dans les versions supérieures, mais il peut y avoir une manière préférée d'enregistrer le filtre global dans les versions ultérieures de MVC.
Rob Church
il suffit de noter sa solution WEB API 2 uniquement. pas pour WEB API 1.
Samih A
5

Après avoir lu chaque réponse et les avoir essayées, aucune d'elles ne m'a aidé. Ce que j'ai trouvé en cherchant ailleurs, c'est que vous pouvez créer un attribut personnalisé que vous pouvez ensuite ajouter à votre contrôleur. Il écrase ceux EnableCors et y ajoute les domaines en liste blanche.

Cette solution fonctionne bien car elle vous permet d'avoir les domaines en liste blanche dans le webconfig (appsettings) au lieu de les coder dans l'attribut EnableCors sur votre contrôleur.

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

J'ai trouvé ce guide en ligne et cela a fonctionné comme un charme:

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

Je pensais que je laisserais ça ici pour toute personne dans le besoin.

Helpha
la source
Ceci est une réponse par lien uniquement. Veuillez plutôt faire en sorte que la réponse soit autonome.
Unslander Monica
1
Ok, je suis nouveau ici, est-ce plus ce que ça pourrait être?
Helpha
3

J'ai réussi à résoudre ce problème dans le code de traitement des demandes en suivant les conseils de «monsur».

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

WebOperationContext.Current.OutgoingResponse.Headers.Add("Access-Control-Allow-Origin", origin);
bsandhu
la source
C'est la manière de faire en webform par exemple. Utilisez simplement Request.Headers lorsque disponible. Et, si nécessaire, utilisez une liste blanche pour filtrer uniquement les domaines autorisés.
AFract
3
C'est aussi bon que d'ajouter <add name = "Access-Control-Allow-Origin" value = "*" /> dans le fichier web.config
Isaiah4110
3

Pour IIS 7.5+, vous pouvez utiliser le module IIS CORS: https://www.iis.net/downloads/microsoft/iis-cors-module

Votre web.config devrait ressembler à ceci:

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

Vous pouvez trouver la référence de configuration ici: https://docs.microsoft.com/en-us/iis/extensions/cors-module/cors-module-configuration-reference

Mario Arturo
la source
Si cela fonctionne comme il le dit, j'aimerais que vous ayez publié ceci il y a 3 ans! Whoa!
Michael le
1

Vous pouvez ajouter ce code à votre projet webapi asp.net

dans le fichier 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
la source
0

Vous pouvez utiliser le middleware owin pour définir une stratégie cors dans laquelle vous pouvez définir plusieurs origines 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
la source
-3

Tu a juste besoins:

  • ajoutez un Global.asax à votre projet,
  • supprimer <add name="Access-Control-Allow-Origin" value="*" />de votre web.config.
  • ensuite, ajoutez ceci dans la Application_BeginRequestméthode 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();
    }

J'espère que cette aide. cela fonctionne pour moi.

joanrm20
la source
L'ajout de "...- Origine: *" fonctionne sauf lorsque vous autorisez les informations d'identification. Si le paramètre allow-credentials est défini sur true, vous devez spécifier un domaine (pas simplement *). C'est là que réside le nœud de ce problème. Sinon, vous pouvez simplement spécifier "... allow-credentials: false" et en finir.
Richard