访问控制允许起源多个起源域?

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

我知道*,但它太开放了。我真的想只允许几个域。

举个例子,像这样:

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

我尝试过上面的代码,但它似乎在Firefox中不起作用。

是否可以指定多个域,或者我只能使用一个?

1054819 次浏览

听起来推荐的方法是让您的服务器从客户端读取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=$0Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOriginHeader merge Vary Origin</IfModule></FilesMatch>

有一个缺点你应该知道:一旦你的源文件到CDN(或任何其他不允许脚本的服务器),或者如果你的文件缓存在代理,改变基于“起源”请求头的响应将不起作用。

答案似乎是使用标题不止一次。也就是说,而不是发送

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

发送

Access-Control-Allow-Origin: http://domain1.exampleAccess-Control-Allow-Origin: http://domain2.exampleAccess-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作为第一个参数。

我在使用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中更改正则表达式。

我在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=$0Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is

当输入.htaccess时,它肯定会工作。

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

location /fonts {# this will echo back the origin headerif ($http_origin ~ "example.org$") {add_header "Access-Control-Allow-Origin" $http_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

由此,我认为不同的起源被承认,应该是空间分隔

这是我为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标头的值,而不是返回*通配符。

并非所有浏览器都使用HTTP_ORIGIN。HTTP_ORIGIN有多安全?对我来说,它在FF中是空的。
我有我允许访问我的站点的站点发送一个站点ID,然后我检查我的数据库中的记录与该ID并获得SITE_URL列值(www.yoursite.com)。

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

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

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

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

Google在通过SSL投放广告RFC本身中的语法上的支持答案似乎表明您可以对URL进行空间分隔。不确定这在不同浏览器中的支持程度。

对于多个域,在您的.htaccess中:

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

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

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

对于Nginx用户来说,允许多个域的CORS。我喜欢@mar的例子,尽管他的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”标头。

我很难为运行HTTPS的域设置此功能,所以我想我会分享解决方案。我在httpd.conf文件中使用了以下指令:

    <FilesMatch "\.(ttf|otf|eot|woff)$">SetEnvIf Origin "^http(s)?://(.+\.)?example\.com$" AccessControlAllowOrigin=$0Header 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 headerssudo service apache2 reload

以下是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$2Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOriginHeader set Access-Control-Allow-Credentials true</IfModule></FilesMatch>

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

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

对于安装了URL重写2.0模块的IIS 7.5+,请参阅这个SO答案

这里有一个解决方案,Java网络应用程序,根据答案从yesThatGuy。

我正在使用泽西REST 1. x

将web.xml配置为了解泽西岛REST和CORS响应过滤器

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

这是CORS响应过滤器的代码

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{
@Overridepublic 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;}}

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

我的Nginx配置的相关部分:

if ($http_origin ~* (https?://.*\.mydomain\.com(:[0-9]+)?)) {set $cors "true";}if ($http_origin ~* (https?://.*\.my-other-domain\.com(:[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';}

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 deliveryresponse["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

为了方便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动词。

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

例如Google中的CTRL+SHIFT+DELChrome删除缓存。

这有助于我在尝试了许多纯.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=$0Header add Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin</IfModule></FilesMatch>

还请注意,许多解决方案都说你必须输入Header set ...,但它是Header add ...。希望这能帮助像我这样的人在几个小时内遇到同样的麻烦。

为了给. NET应用程序提供一个相当简单的复制/粘贴,我编写了这个代码来从global.asax文件中启用CORS。这段代码遵循了当前接受的答案中给出的建议,将请求中给出的任何来源反映到响应中。这在不使用它的情况下有效地实现了“*”。

这样做的原因是它支持多个其他CORS功能,包括发送AJAX XMLHttpRequest的能力,其中'with Cre的属性设置为'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"]);elseResponse.AddHeader("Access-Control-Allow-Origin" , "*");}}

对于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();});

下面的答案是针对C#的,但这个概念应该适用于所有不同的平台。

要允许来自Web API的跨源请求,您需要允许对您的应用程序的选项请求并在控制器级别添加以下注释。

[EnableCors(UrlString、标头、方法)]现在只能传递源s字符串。因此,如果您想在请求中传递多个URL,请将其作为逗号分隔值传递。

UrlString="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}

PHP代码:

$httpOrigin = isset($_SERVER['HTTP_ORIGIN']) ? $_SERVER['HTTP_ORIGIN'] : null;if (in_array($httpOrigin, ['http://localhost:9000', // Co-worker dev-server'http://127.0.0.1:9001', // My dev-server])) header("Access-Control-Allow-Origin: ${httpOrigin}");header('Access-Control-Allow-Credentials: true');

AWS Lambda/API网关

有关如何在无服务器AWS Lambda和API Gateway上配置多个源的信息-尽管这是一个相当大的解决方案,人们会觉得应该非常简单-请参阅此处:

https://stackoverflow.com/a/41708323/1624933


目前无法在API网关中配置多个源,请参阅此处:https://docs.aws.amazon.com/apigateway/latest/developerguide/how-to-cors-console.html),但建议(在上面的答案中)是:

  • 检查浏览器发送的Origin标头
  • 把它和起源的白名单核对一下
  • 如果匹配,则返回传入的Origin作为Access-Control-Allow-Origin标头,否则返回占位符(默认源)。

简单的解决方案显然使所有(*)像这样:

exports.handler = async (event) => {const response = {statusCode: 200,headers: {"Access-Control-Allow-Origin": "*","Access-Control-Allow-Credentials" : true // Required for cookies, authorization headers with HTTPS},body: JSON.stringify([{

但在API网关端执行此操作可能更好(请参阅上面的第二个链接)。

我有https://stackoverflow.com/a/7454204/13779574这段代码运行良好,但当用户进入该页面时会出现错误。我用这段代码解决了这个问题。

if (isset($_SERVER['HTTP_ORIGIN'])) {$http_origin = $_SERVER['HTTP_ORIGIN'];if ($http_origin == "http://localhost:3000" || $http_origin == "http://api.loc/"){header("Access-Control-Allow-Origin: $http_origin");}}

我也遇到了同样的问题。我的客户端在9097上,api网关在9098上,微服务在……其实我用的是Spring Cloud Api网关
在我的网关yml文件中,我允许交叉源-

http://localhost:9097

在我的微服务中,我也使用@CrossOrigin

当客户端向api网关发送请求时,两个“Access-Control-Allow-Origin”标头正在响应[一个来自api yml文件,一个来自microservice@CrossOrigin]所以浏览器阻止请求

我解决了这个问题

    @Beanpublic RouteLocator getRL(RouteLocatorBuilder builder) {    
return  builder.routes()    
.route(p->"/friendlist","/guest/**").filters(f ->{//f.removeResponseHeader("Access-Control-Allow-Origin");//f.addResponseHeader("Access-Control-Allow-Origin","http://localhost:9097");f.setResponseHeader("Access-Control-Allow-Origin","http://localhost:9097");return f;}).uri("lb://OLD-SERVICE")        
        
).build();}