首页 文章

Access-Control-Allow-Origin多个源域?

提问于
浏览
869

有没有办法允许多个跨域使用 Access-Control-Allow-Origin 标头?

我知道 * ,但它太开放了 . 我真的想只允许一些域名 .

举个例子,像这样:

Access-Control-Allow-Origin: http://domain1.example, http://domain2.example

我已经尝试了上面的代码,但它似乎不适用于Firefox .

是否可以指定多个域,或者我只坚持一个?

26 回答

  • 14

    听起来像推荐的方法是让服务器从客户端读取Origin头,将其与您想要允许的域列表进行比较,如果匹配,则将 Origin 头的值回显给客户端作为响应中的 Access-Control-Allow-Origin Headers .

    使用 .htaccess ,您可以这样做:

    # ----------------------------------------------------------------------
    # Allow loading of external fonts
    # ----------------------------------------------------------------------
    <FilesMatch "\.(ttf|otf|eot|woff)$">
        <IfModule mod_headers.c>
            SetEnvIf Origin "http(s)?://(www\.)?(google.com|staging.google.com|development.google.com|otherdomain.example|dev02.otherdomain.example)$" AccessControlAllowOrigin=$0
            Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
            Header merge Vary Origin
        </IfModule>
    </FilesMatch>
    
  • 1

    我在PHP中使用的另一个解决方案:

    $http_origin = $_SERVER['HTTP_ORIGIN'];
    
    if ($http_origin == "http://www.domain1.com" || $http_origin == "http://www.domain2.com" || $http_origin == "http://www.domain3.com")
    {  
        header("Access-Control-Allow-Origin: $http_origin");
    }
    
  • 84

    这对我有用:

    SetEnvIf Origin "^http(s)?://(.+\.)?(domain\.example|domain2\.example)$" origin_is=$0 
    Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
    

    当放入 .htaccess 时,它肯定会起作用 .

  • -2

    我遇到了与woff-fonts相同的问题,多个子域必须具有访问权限 . 为了允许子域名,我在httpd.conf中添加了这样的内容:

    SetEnvIf Origin "^(.*\.example\.com)$" ORIGIN_SUB_DOMAIN=$1
    <FilesMatch "\.woff$">
        Header set Access-Control-Allow-Origin "%{ORIGIN_SUB_DOMAIN}e" env=ORIGIN_SUB_DOMAIN
    </FilesMatch>
    

    对于多个域,您只需更改 SetEnvIf 中的正则表达式 .

  • 5

    以下是如果它与您的域与Nginx匹配时如何回显Origin头,如果您想要为多个子域提供字体,这将非常有用:

    location /fonts {
        # this will echo back the origin header
        if ($http_origin ~ "example.org$") {
            add_header "Access-Control-Allow-Origin" $http_origin;
        }
    }
    
  • 4

    这是我为AJAX请求的PHP应用程序所做的

    $request_headers        = apache_request_headers();
    $http_origin            = $request_headers['Origin'];
    $allowed_http_origins   = array(
                                "http://myDumbDomain.example"   ,
                                "http://anotherDumbDomain.example"  ,
                                "http://localhost"  ,
                              );
    if (in_array($http_origin, $allowed_http_origins)){  
        @header("Access-Control-Allow-Origin: " . $http_origin);
    }
    

    如果我的服务器允许请求源,则返回 $http_origin 本身作为 Access-Control-Allow-Origin 标头的值,而不是返回 * 通配符 .

  • 18

    您应该注意一个缺点:只要您将文件外包到CDN(或任何其他不允许编写脚本的服务器)或者您的文件缓存在代理上,就会根据“Origin”更改响应请求标头将无法正常工作 .

  • 0

    对于多个域,请在_827918中:

    <IfModule mod_headers.c>
        SetEnvIf Origin "http(s)?://(www\.)?(domain1.example|domain2.example)$" AccessControlAllowOrigin=$0$1
        Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
        Header set Access-Control-Allow-Credentials true
    </IfModule>
    
  • 13

    对于安装了URL Rewrite 2.0模块的IIS 7.5,请参阅this SO answer

  • 750

    对于Nginx用户,允许CORS用于多个域 . 我喜欢@ marshall的例子,虽然他的anwers只匹配一个域 . 要匹配域和子域列表,此正则表达式可以轻松使用字体:

    location ~* \.(?:ttf|ttc|otf|eot|woff|woff2)$ {
       if ( $http_origin ~* (https?://(.+\.)?(domain1|domain2|domain3)\.(?:me|co|com)$) ) {
          add_header "Access-Control-Allow-Origin" "$http_origin";
       }
    }
    

    这将只回显与给定的域列表匹配的“Access-Control-Allow-Origin”标头 .

  • 1

    这是基于yesthatguy的答案的Java Web应用程序解决方案 .

    我正在使用Jersey REST 1.x.

    配置web.xml以了解Jersey REST和CORSResponseFilter

    <!-- Jersey REST config -->
      <servlet>    
        <servlet-name>JAX-RS Servlet</servlet-name>
        <servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
        <init-param> 
            <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
            <param-value>true</param-value>
        </init-param>
        <init-param>
          <param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
          <param-value>com.your.package.CORSResponseFilter</param-value>
        </init-param>   
        <init-param>
            <param-name>com.sun.jersey.config.property.packages</param-name>
            <param-value>com.your.package</param-value>
        </init-param>        
        <load-on-startup>1</load-on-startup>
      </servlet>
      <servlet-mapping>
        <servlet-name>JAX-RS Servlet</servlet-name>
        <url-pattern>/ws/*</url-pattern>
      </servlet-mapping>
    

    这是CORSResponseFilter的代码

    import com.sun.jersey.spi.container.ContainerRequest;
    import com.sun.jersey.spi.container.ContainerResponse;
    import com.sun.jersey.spi.container.ContainerResponseFilter;
    
    
    public class CORSResponseFilter implements ContainerResponseFilter{
    
    @Override
    public ContainerResponse filter(ContainerRequest request,
            ContainerResponse response) {
    
        String[] allowDomain = {"http://localhost:9000","https://my.domain.example"};
        Set<String> allowedOrigins = new HashSet<String>(Arrays.asList (allowDomain));                  
    
        String originHeader = request.getHeaderValue("Origin");
    
        if(allowedOrigins.contains(originHeader)) {
            response.getHttpHeaders().add("Access-Control-Allow-Origin", originHeader);
    
            response.getHttpHeaders().add("Access-Control-Allow-Headers",
                    "origin, content-type, accept, authorization");
            response.getHttpHeaders().add("Access-Control-Allow-Credentials", "true");
            response.getHttpHeaders().add("Access-Control-Allow-Methods",
                    "GET, POST, PUT, DELETE, OPTIONS, HEAD");
        }
    
        return response;
    }
    
    }
    
  • 7

    如上所述, Access-Control-Allow-Origin 应该是唯一的,如果您位于CDN(内容分发网络)后面, Vary 应设置为 Origin .

    我的Nginx配置的相关部分:

    if ($http_origin ~* (https?://.*\.mydomain.example(:[0-9]+)?)) {
      set $cors "true";
    }
    if ($cors = "true") {
      add_header 'Access-Control-Allow-Origin' "$http_origin";
      add_header 'X-Frame-Options' "ALLOW FROM $http_origin";
      add_header 'Access-Control-Allow-Credentials' 'true';
      add_header 'Vary' 'Origin';
    }
    
  • 1

    也许我错了,但据我所知 Access-Control-Allow-Origin 有一个 "origin-list" 作为参数 .

    definition origin-list 是:

    origin            = "origin" ":" 1*WSP [ "null" / origin-list ]
    origin-list       = serialized-origin *( 1*WSP serialized-origin )
    serialized-origin = scheme "://" host [ ":" port ]
                      ; <scheme>, <host>, <port> productions from RFC3986
    

    从此,我认为不同的起源被承认,应该是 space separated .

  • 57

    我努力为运行HTTPS的域设置这个,所以我想我会分享解决方案 . 我在httpd.conf文件中使用了以下指令:

    <FilesMatch "\.(ttf|otf|eot|woff)$">
                SetEnvIf Origin "^http(s)?://(.+\.)?example\.com$" AccessControlAllowOrigin=$0
                Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
        </FilesMatch>
    

    example.com 更改为您的域名 . 在httpd.conf文件中的 <VirtualHost x.x.x.x:xx> 中添加它 . 请注意,如果您的 VirtualHost 有一个端口后缀(例如 :80 ),那么该指令将不适用于HTTPS,因此您还需要转到/ etc / apache2 / sites-available / default-ssl并在该文件中添加相同的指令,在 <VirtualHost _default_:443> 部分内 .

    更新配置文件后,您需要在终端中运行以下命令:

    a2enmod headers
    sudo service apache2 reload
    
  • 174

    如果您遇到字体问题,请使用:

    <FilesMatch "\.(ttf|ttc|otf|eot|woff)$">
        <IfModule mod_headers>
            Header set Access-Control-Allow-Origin "*"
        </IfModule>
    </FilesMatch>
    
  • 1

    并非所有浏览器都使用HTTP_ORIGIN . How secure is HTTP_ORIGIN?对我来说,它在FF中显示为空 .
    我有允许访问我的网站的网站通过网站ID发送,然后检查我的数据库以获取具有该ID的记录并获得SITE_URL列值(www.yoursite.com) .

    header('Access-Control-Allow-Origin: http://'.$row['SITE_URL']);
    

    即使通过有效站点ID发送,请求也需要来自与该站点ID关联的数据库中列出的域 .

  • 99

    这是apache的扩展选项,包括一些最新的和计划的字体定义:

    <FilesMatch "\.(ttf|otf|eot|woff|woff2|sfnt|svg)$">
        <IfModule mod_headers.c>
            SetEnvIf Origin "^http(s)?://(.+\.)?(domainname1|domainname2|domainname3)\.(?:com|net|org)$" AccessControlAllowOrigin=$0$1$2
            Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
            Header set Access-Control-Allow-Credentials true
        </IfModule>
    </FilesMatch>
    
  • 22

    Django还有一个答案 . 要使用单个视图允许来自多个域的CORS,这是我的代码:

    def my_view(request):
        if 'HTTP_ORIGIN' in request.META.keys() and request.META['HTTP_ORIGIN'] in ['http://allowed-unsecure-domain.com', 'https://allowed-secure-domain.com', ...]:
            response = my_view_response() # Create your desired response data: JsonResponse, HttpResponse...
            # Then add CORS headers for access from delivery
            response["Access-Control-Allow-Origin"] = request.META['HTTP_ORIGIN']
            response["Access-Control-Allow-Methods"] = "GET" # "GET, POST, PUT, DELETE, OPTIONS, HEAD"
            response["Access-Control-Max-Age"] = "1000"  
            response["Access-Control-Allow-Headers"] = "*"  
            return response
    
  • 16

    对于.NET应用程序的相当容易的复制/粘贴,我写了这个来从global.asax文件中启用CORS . 此代码遵循当前接受的答案中给出的建议,反映请求中给出的任何原点返回到响应中 . 这有效地实现了'*'而不使用它 . 这样做的原因是它支持多种其他CORS功能,包括能够发送带有'withCredentials'属性设置为'true'的AJAX XMLHttpRequest .

    void Application_BeginRequest(object sender, EventArgs e)
    {
        if (Request.HttpMethod == "OPTIONS")
        {
            Response.AddHeader("Access-Control-Allow-Methods", "GET, POST");
            Response.AddHeader("Access-Control-Allow-Headers", "Content-Type, Accept");
            Response.AddHeader("Access-Control-Max-Age", "1728000");
            Response.End();
        }
        else
        {
            Response.AddHeader("Access-Control-Allow-Credentials", "true");
    
            if (Request.Headers["Origin"] != null)
                Response.AddHeader("Access-Control-Allow-Origin" , Request.Headers["Origin"]);
            else
                Response.AddHeader("Access-Control-Allow-Origin" , "*");
        }
    }
    
  • 10

    为了便于ASMX服务的多域访问,我在global.asax文件中创建了这个函数:

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        string CORSServices = "/account.asmx|/account2.asmx";
        if (CORSServices.IndexOf(HttpContext.Current.Request.Url.AbsolutePath) > -1)
        {
            string allowedDomains = "http://xxx.yyy.example|http://aaa.bbb.example";
    
            if(allowedDomains.IndexOf(HttpContext.Current.Request.Headers["Origin"]) > -1)
                HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", HttpContext.Current.Request.Headers["Origin"]);
    
            if(HttpContext.Current.Request.HttpMethod == "OPTIONS")
                HttpContext.Current.Response.End();
        }
    }
    

    这允许CORS处理 OPTIONS 动词也 .

  • 6

    Google对serving ads over SSLgrammar in the RFC itself的支持回答似乎表明您可以用空格分隔网址 . 不确定这在不同浏览器中的支持程度如何 .

  • 1

    如果您尝试使用像我这样的代码示例来使其使用CORS,那么值得一提的是,您必须首先清除缓存以尝试它是否真的有效,类似于旧图像仍然存在时的问题,即使它是在服务器上删除(因为它仍保存在缓存中) .

    例如,Google Chrome中的CTRL SHIFT DEL可删除您的缓存 .

    这有助于我在尝试了许多纯粹的 .htaccess 解决方案后使用此代码,这似乎是唯一一个有效的(至少对我而言):

    Header add Access-Control-Allow-Origin "http://google.com"
        Header add Access-Control-Allow-Headers "authorization, origin, user-token, x-requested-with, content-type"
        Header add Access-Control-Allow-Methods "PUT, GET, POST, DELETE, OPTIONS"
    
        <FilesMatch "\.(ttf|otf|eot|woff)$">
            <IfModule mod_headers.c>
                SetEnvIf Origin "http(s)?://(www\.)?(google.com|staging.google.com|development.google.com|otherdomain.com|dev02.otherdomain.net)$" AccessControlAllowOrigin=$0
                Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin
            </IfModule>
        </FilesMatch>
    

    还要注意,它被广泛传播,许多解决方案说你必须输入 Header set ... 但它是 Header add ... . 希望这能帮助像我一样有几个小时遇到同样麻烦的人 .

  • 0

    更灵活的方法是使用Apache 2.4的表达式 . 您可以匹配域,路径以及几乎所有其他请求变量 . 尽管响应是 * ,但是接收该响应的唯一请求者仍然是满足要求的请求者 .

    <IfModule mod_headers.c>
        <If "%{HTTP:Host} =~ /\\byourdomain\\.example$/i">
            Header set Access-Control-Allow-Origin "*"
        </If>
    </IfModule>
    
  • 0

    用于匹配子域的PHP代码示例 .

    if( preg_match("/http:\/\/(.*?)\.yourdomain.example/", $_SERVER['HTTP_ORIGIN'], $matches )) {
            $theMatch = $matches[0];
            header('Access-Control-Allow-Origin: ' . $theMatch);
    }
    
  • -1

    答案似乎是不止一次使用 Headers . 也就是说,而不是发送

    Access-Control-Allow-Origin: http://domain1.example, http://domain2.example, http://domain3.example
    

    发送

    Access-Control-Allow-Origin: http://domain1.example
    Access-Control-Allow-Origin: http://domain2.example
    Access-Control-Allow-Origin: http://domain3.example
    

    在Apache上,您可以使用 mod_headershttpd.conf <VirtualHost> 部分或 .htaccess 文件中执行此操作,并使用以下语法:

    Header add Access-Control-Allow-Origin "http://domain1.example"
    Header add Access-Control-Allow-Origin "http://domain2.example"
    Header add Access-Control-Allow-Origin "http://domain3.example"
    

    诀窍是使用 add 而不是 append 作为第一个参数 .

  • 0

    我们也可以在Asp.net应用程序的Global.asax文件中设置它 .

    protected void Application_BeginRequest(object sender, EventArgs e)
        {
    
        // enable CORS
        HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "https://www.youtube.com");
    
        }
    

相关问题