Вопрос

Есть ли способ разрешить несколько перекрестных доменов с помощью Access-Control-Allow-Origin заголовок?

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

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

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

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

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

Это было полезно?

Решение

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

Другие советы

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

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

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, это точно сработает.

У меня была такая же проблема с 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.

Вот как вернуть заголовок Origin, если он соответствует вашему домену с Nginx. Это полезно, если вы хотите использовать шрифт в нескольких поддоменах:

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

Вот что я сделал для 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 заголовок вместо возврата * подстановочный знак.

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

Для нескольких доменов в вашем .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>

Для пользователей 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», которые соответствуют заданному списку доменов.

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

Вот решение для веб-приложения 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;
}

}

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

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

Возможно я ошибаюсь, но насколько я понимаю 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

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

Мне было сложно настроить это для домена с 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/apache2/sites-available/default-ssl и добавьте ту же директиву в этот файл внутри <VirtualHost _default_:443> раздел.

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

a2enmod headers
sudo service apache2 reload

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

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

Для приложений 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();
});

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

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

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

Вот расширенный вариант 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>

И еще один ответ в Джанго.Чтобы иметь одно представление, позволяющее использовать 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

Для довольно простого копирования и вставки в приложения .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" , "*");
    }
}

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

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

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

Более гибкий подход — использовать выражения 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>

Ответ службы поддержки Google на показ рекламы через SSL и грамматика в самом RFC казалось бы, указывает на то, что вы можете разделять URL-адреса пробелами.Не уверен, насколько хорошо это поддерживается в разных браузерах.

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

Например CTRL + Shift + ДЕЛ в 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 ....Надеюсь, это поможет кому-то, испытывающему те же проблемы, что и я, уже несколько часов.

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

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

ENablecors (urlString, заголовок, метод)] Теперь происхождение может быть передано только как строка.ТАК, если вы хотите передать более одного URL-адреса в запросе, передайте его как значение, разделенное запятыми.

УрлСтрока = "https://a.hello.com, https://b.hello.com"

Для заголовка 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
  }

Мы также можем установить это в файле 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");

    }

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

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 в качестве первого аргумента.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top