Access-Control-Allow-Origin Multiple Origin Domains?
有没有办法允许使用
我知道
举个例子,像这样:
1 | Access-Control-Allow-Origin: http://domain1.example, http://domain2.example |
我已经尝试过上面的代码,但它似乎不适用于Firefox。
是否可以指定多个域,或者我只坚持一个?
听起来像推荐的方法是让服务器从客户端读取Origin头,将其与您想要允许的域列表进行比较,如果匹配,则将
使用
1 2 3 4 5 6 7 8 9 10 | # ---------------------------------------------------------------------- # 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中使用的另一个解决方案:
1 2 3 4 5 6 | $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"); } |
这对我有用:
1 2 | SetEnvIf Origin"^http(s)?://(.+\.)?(domain\.example|domain2\.example)$" origin_is=$0 Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is |
当放入
我有与woff-fonts相同的问题,多个子域必须具有访问权限。为了允许子域名,我在httpd.conf中添加了这样的内容:
1 2 3 4 | 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> |
对于多个域,您只需更改
以下是如果它与您的域与Nginx匹配时如何回显Origin头,如果您想要为多个子域提供字体,这非常有用:
1 2 3 4 5 6 | location /fonts { # this will echo back the origin header if ($http_origin ~"example.org$") { add_header"Access-Control-Allow-Origin" $http_origin; } } |
这是我为AJAX请求的PHP应用程序所做的
1 2 3 4 5 6 7 8 9 10 | $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); } |
如果我的服务器允许请求源,则将
您应该注意一个缺点:只要您将文件外包到CDN(或任何其他不允许编写脚本的服务器)或者您的文件缓存在代理上,就会根据"Origin"更改响应请求标头将无法正常工作。
对于多个域,在
1 2 3 4 5 | <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的例子,尽管他的答案只匹配一个域名。要匹配域和子域列表,此正则表达式可以轻松使用字体:
1 2 3 4 5 | 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"标头。
对于安装了URL Rewrite 2.0模块的IIS 7.5+,请参阅此SO答案
这是基于yesthatguy的答案的Java Web应用程序解决方案。
我正在使用Jersey REST 1.x.
配置web.xml以了解Jersey REST和CORSResponseFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <!-- 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的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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; } } |
如上所述,如果您位于CDN(内容交付网络)之后,
我的Nginx配置的相关部分:
1 2 3 4 5 6 7 8 9 | 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 2 3 4 | 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文件中使用了以下指令:
1 2 3 4 | <FilesMatch"\.(ttf|otf|eot|woff)$"> SetEnvIf Origin"^http(s)?://(.+\.)?example\.com$" AccessControlAllowOrigin=$0 Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin </FilesMatch> |
将
更新配置文件后,您需要在终端中运行以下命令:
1 2 | a2enmod headers sudo service apache2 reload |
如果您遇到字体问题,请使用:
1 2 3 4 5 | <FilesMatch"\.(ttf|ttc|otf|eot|woff)$"> <IfModule mod_headers> Header set Access-Control-Allow-Origin"*" </IfModule> </FilesMatch> |
对于ExpressJS应用程序,您可以使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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(); }); |
这是apache的扩展选项,包括一些最新的和计划的字体定义:
1 2 3 4 5 6 7 | <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> |
用于匹配子域的PHP代码示例。
1 2 3 4 | if( preg_match("/http:\/\/(.*?)\.yourdomain.example/", $_SERVER['HTTP_ORIGIN'], $matches )) { $theMatch = $matches[0]; header('Access-Control-Allow-Origin: ' . $theMatch); } |
所有浏览器都不使用HTTP_ORIGIN。 HTTP_ORIGIN的安全性如何?对我来说,它在FF中是空的。
我有允许访问我的网站的网站通过网站ID发送,然后检查我的数据库以获取具有该ID的记录并获得SITE_URL列值(www.yoursite.com)。
1 | header('Access-Control-Allow-Origin: http://'.$row['SITE_URL']); |
即使通过有效站点ID发送,请求也必须来自与该站点ID关联的数据库中列出的域。
Django还有一个答案。要使用单个视图允许来自多个域的CORS,这是我的代码:
1 2 3 4 5 6 7 8 9 | 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 |
为了便于ASMX服务的多域访问,我在global.asax文件中创建了这个函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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(); } } |
这也允许对
对于.NET应用程序的相当容易的复制/粘贴,我写了这个来从global.asax文件中启用CORS。此代码遵循当前接受的答案中给出的建议,反映请求中给出的任何原点返回到响应中。这有效地实现了'*'而不使用它。这样做的原因是它支持多种其他CORS功能,包括能够发送带有'withCredentials'属性设置为'true'的AJAX XMLHttpRequest。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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" ,"*"); } } |
更灵活的方法是使用Apache 2.4的表达式。您可以匹配域,路径以及几乎所有其他请求变量。尽管对所有人的响应都是
1 2 3 4 5 | <IfModule mod_headers.c> <If"%{HTTP:Host} =~ /\\bcdndomain\\.example$/i && %{HTTP:Origin} =~ /\\bmaindomain\\.example$/i"> Header set Access-Control-Allow-Origin"*" </If> </IfModule> |
只能为Access-Control-Allow-Origin标头指定单个原点。但您可以根据请求在响应中设置原点。另外不要忘记设置Vary标头。在PHP中我会做以下事情:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /** * 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 } |
Google通过SSL提供广告的支持答案以及RFC本身的语法似乎表明您可以空间分隔URL。不确定这在不同浏览器中的支持程度如何。
如果您尝试使用像我这样的代码示例来使其使用CORS,那么值得一提的是,您必须首先清除缓存以尝试它是否真的有效,类似于旧图像仍然存在时的问题,即使它是在服务器上删除(因为它仍保存在缓存中)。
例如,Google Chrome中的CTRL + SHIFT + DEL可删除您的缓存。
这有助于我在尝试许多纯
1 2 3 4 5 6 7 8 9 10 | 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> |
另请注意,很多解决方案都说您必须键入
以下答案特定于C#,但该概念应适用于所有不同的平台。
要允许来自Web API的跨源请求,您需要允许对应用程序的Option请求和在控制器级别下面的注释添加。
[EnableCors(UrlString,Header,Method)]
现在,原点只能传递一个字符串。因此,如果要在请求中传递多个URL,请将其作为逗号分隔值传递。
UrlString ="https://a.hello.com,https://b.hello.com"
我们也可以在Asp.net应用程序的Global.asax文件中设置它。
1 2 3 4 5 6 7 | protected void Application_BeginRequest(object sender, EventArgs e) { // enable CORS HttpContext.Current.Response.AddHeader("Access-Control-Allow-Origin","https://www.youtube.com"); } |
答案似乎是不止一次使用标题。也就是说,而不是发送
1 | Access-Control-Allow-Origin: http://domain1.example, http://domain2.example, http://domain3.example |
发送
1 2 3 | Access-Control-Allow-Origin: http://domain1.example Access-Control-Allow-Origin: http://domain2.example Access-Control-Allow-Origin: http://domain3.example |
在Apache上,您可以使用
1 2 3 | 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" |
诀窍是使用