Access-Control-Allow-Origin Несколько доменов происхождения?

Есть ли способ разрешить несколько кросс-доменов, используя заголовок Access-Control-Allow-Origin?

Я знаю о *, но он слишком открыт. Я действительно хочу разрешить только пару доменов.

Как пример, что-то вроде этого:

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

Я пробовал приведенный выше код, но он не работает в Firefox.

Можно ли указать несколько доменов или я застрял только на одном?

Ответ 1

Похоже, рекомендуемый способ сделать это - заставить ваш сервер читать заголовок Origin с клиента, сравнить его со списком доменов, которые вы хотели бы разрешить, и, если он совпадает, передать значение заголовка Origin обратно клиенту в качестве заголовка Access-Control-Allow-Origin в ответе.

С .htaccess вы можете сделать это так:

# ----------------------------------------------------------------------
# Allow loading of external fonts
# ----------------------------------------------------------------------
<FilesMatch "\.(ttf|otf|eot|woff|woff2)$">
    <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>

Ответ 2

Другое решение, которое я использую в 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");
}

Ответ 3

Это сработало для меня:

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, он будет работать наверняка.

Ответ 4

У меня была та же проблема с woff-шрифтами, у нескольких поддоменов был доступ. Чтобы разрешить субдомены, я добавил что-то вроде этого в 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

Здесь, как повторить заголовок Origin, если он соответствует вашему домену с помощью Nginx, это полезно, если вы хотите обслуживать несколько поддоменов шрифта:

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

Ответ 6

Вот что я сделал для приложения PHP, которое запрашивает AJAX

$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 вместо возврата символа подстановки *.

Ответ 7

Есть один недостаток, о котором вам следует знать: как только вы выберете исходные файлы на CDN (или на любой другой сервер, который не позволяет создавать сценарии), или если ваши файлы кэшированы на прокси-сервере, изменение ответа на основе Заголовок запроса "Origin" не будет работать.

Ответ 8

Для нескольких доменов в вашем .htaccess:

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

Ответ 9

Для пользователей Nginx разрешить CORS для нескольких доменов. Мне нравится пример @marshall, хотя его антверпы соответствуют только одному домену. Для соответствия списку домена и поддомена это регулярное выражение облегчает работу со шрифтами:

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", которые соответствуют указанному списку доменов.

Ответ 10

Для IIS 7.5+ с установленным модулем URL Rewrite 2.0 см. этот ответ SO

Ответ 11

Вот решение для веб-приложения Java, основанное на ответе yesthatguy.

Я использую Джерси REST 1.x

Сконфигурируйте web.xml, чтобы знать о 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;
}

}

Ответ 12

Как упомянуто выше, Access-Control-Allow-Origin должен быть уникальным, а Vary должен быть установлен на Origin если вы находитесь за CDN (Content Delivery Network).

Соответствующая часть моей конфигурации 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';
}

Ответ 13

Возможно, я ошибаюсь, но насколько я вижу, Access-Control-Allow-Origin имеет "origin-list" качестве параметра.

По определению 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

И из этого я утверждаю, что допускается различное происхождение, и оно должно быть разделено пробелами

Ответ 14

Я изо всех сил пытался настроить это для домена, использующего 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 на свое доменное имя. Добавьте это внутри <VirtualHost x.x.x.x:xx> в свой файл httpd.conf. Обратите внимание, что если ваш VirtualHost имеет суффикс порта (например, :80), эта директива не будет применяться к HTTPS, поэтому вам также нужно перейти в /etc/apache 2/sites-available/default-ssl и добавить тот же директивы в этом файле, внутри раздела <VirtualHost _default_:443>.

После обновления файлов конфигурации вам необходимо будет выполнить следующие команды в терминале:

a2enmod headers
sudo service apache2 reload

Ответ 15

Если у вас возникли проблемы со шрифтами, используйте:

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

Ответ 16

Для приложений ExpressJS вы можете использовать:

app.use((req, res, next) => {
    const corsWhitelist = [
        'https://domain1.example',
        'https://domain2.example',
        'https://domain3.example'
    ];
    if (corsWhitelist.indexOf(req.headers.origin) !== -1) {
        res.header('Access-Control-Allow-Origin', req.headers.origin);
        res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
    }

    next();
});

Ответ 17

HTTP_ORIGIN не используется всеми браузерами. Насколько безопасен HTTP_ORIGIN? Для меня это пустое в FF.
У меня есть сайты, на которых я разрешаю доступ к моему сайту для отправки по идентификатору сайта, затем я проверяю свою БД для записи с этим идентификатором и получаю значение столбца SITE_URL (www.yoursite.com).

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

Даже если отправка по допустимому идентификатору сайта требует запроса из домена, указанного в моей БД, связанной с этим идентификатором сайта.

Ответ 18

Пример кода PHP для сопоставления поддоменов.

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

Ответ 19

Здесь представлен расширенный вариант для 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>

Ответ 20

И еще один ответ в 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

Ответ 21

Чтобы облегчить многодоменный доступ для службы 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.

Ответ 22

Для довольно простого копирования/вставки для приложений .NET я написал это, чтобы включить CORS из файла global.asax. Этот код следует советам, данным в принятом в настоящее время ответе, отражая происхождение, указанное в запросе в ответе. Это эффективно достигает "*" без его использования. Причина этого заключается в том, что он включает несколько других функций CORS, включая возможность отправки AJAX XMLHttpRequest с атрибутом withCredentials, установленным в значение true.

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" , "*");
    }
}

Ответ 23

Более гибкий подход заключается в использовании выражений Apache 2.4. Вы можете сопоставить домены, пути и практически все остальные переменные запроса. Хотя ответ * для всех, единственными, кто его получает, являются те, кто в любом случае отвечает требованиям.

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

Ответ 25

Если вы попробуете так много примеров кода, как я, чтобы заставить его работать с помощью CORS, стоит упомянуть, что вам нужно сначала очистить кеш, чтобы попробовать, если он действительно работает, похожий на проблемы, например, когда старые изображения все еще присутствуют, даже если он удален на сервере (потому что он все еще сохраняется в вашем кеше).

Например, CTRL + SHIFT + DEL в Google Chrome для удаления кеша.

Это помогло мне использовать этот код, попробовав множество чистых решений .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 .... Надеюсь, что это помогает кому-то, испытывающему те же проблемы в течение нескольких часов, как я.

Ответ 26

Ниже ответ специфичен для С#, но концепция должна быть применима ко всем различным платформам.

Чтобы разрешить перекрестные исходные запросы от веб-API, необходимо разрешить запросы Option для своего приложения и добавить примечание ниже на уровне контроллера.

[EnableCors (UrlString, Header, Method)] Теперь происхождение можно передавать только в виде строки. Поэтому, если вы хотите передать более одного URL-адреса в запросе, передайте его как разделенное запятыми значение.

UrlString = " https://a.hello.com,https://b.hello.com "

Ответ 27

Для заголовка Access-Control-Allow-Origin можно указать только один источник. Но вы можете установить источник в вашем ответе в соответствии с запросом. Также не забудьте установить заголовок Vary. В PHP я бы сделал следующее:

    /**
     * Enable CORS for the passed origins.
     * Adds the Access-Control-Allow-Origin header to the response with the origin that matched the one in the request.
     * @param array $origins
     * @return string|null returns the matched origin or null
     */
    function allowOrigins($origins)
    {
        $val = $_SERVER['HTTP_ORIGIN'] ?? null;
        if (in_array($val, $origins, true)) {
            header('Access-Control-Allow-Origin: '.$val);
            header('Vary: Origin');

            return $val;
        }

        return null;
    }

  if (allowOrigins(['http://localhost', 'https://localhost'])) {
      echo your response here, e.g. token
  }

Ответ 28

Мы также можем установить это в файле Global.asax для приложения Asp.net.

protected void Application_BeginRequest(object sender, EventArgs e)
    {

    // enable CORS
    HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin", "https://www.youtube.com");

    }

Ответ 29

Ответ, похоже, заключается в использовании заголовка более одного раза. То есть вместо отправки

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 вы можете сделать это в разделе httpd.conf <VirtualHost> или в файле .htaccess используя mod_headers и следующий синтаксис:

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.