CORS规范是全有或全无的。它仅支持*
、null
或完整的协议+域名+端口:http://www.w3.org/TR/cors/#access-control-allow-origin-response-header
您的服务器需要使用正则表达式验证来源头,然后在Access-Control-Allow-Origin
响应头中回显来源值。
参考 DaveRandom 的 答案,我也尝试着实验了一下并发现了一个更简单的 Apache 解决方案,它能够动态地将 Access-Control-Allow-Origin
设置为当前特定的协议 + 域名 + 端口,而无需使用任何重写规则:
SetEnvIf Origin ^(https?://.+\.mywebsite\.example(?::\d{1,5})?)$ CORS_ALLOW_ORIGIN=$1
Header append Access-Control-Allow-Origin %{CORS_ALLOW_ORIGIN}e env=CORS_ALLOW_ORIGIN
Header merge Vary "Origin"
就这样了。
那些想要在父域名(例如mywebsite.example
)以及其所有子域名上启用CORS的人只需将第一行中的正则表达式替换为以下内容:
^(https?://(?:.+\.)?mywebsite\.example(?::\d{1,5})?)$
。
注意:为了符合规范要求和正确的缓存行为,始终为启用CORS的资源添加 Vary: Origin
响应头,即使是非CORS请求和来自未被允许的来源(请参见示例原因)。
e
是什么意思?从%{CORS_ALLOW_ORIGIN}e
这一行中? - Nathan//
,因为 Apache 配置文件不使用斜杠分隔的正则表达式。Regexr 抱怨是因为在那种情况下,斜杠具有作为定界符的特殊含义。 - Noyo.htaccess
(或httpd.conf
)解决方案,适用于Apache:<IfModule mod_rewrite.c>
<IfModule mod_headers.c>
# Define the root domain that is allowed
SetEnvIf Origin .+ ACCESS_CONTROL_ROOT=yourdomain.example
# Check that the Origin: matches the defined root domain and capture it in
# an environment var if it does
RewriteEngine On
RewriteCond %{ENV:ACCESS_CONTROL_ROOT} !=""
RewriteCond %{ENV:ACCESS_CONTROL_ORIGIN} =""
RewriteCond %{ENV:ACCESS_CONTROL_ROOT}&%{HTTP:Origin} ^([^&]+)&(https?://(?:.+?\.)?\1(?::\d{1,5})?)$
RewriteRule .* - [E=ACCESS_CONTROL_ORIGIN:%2]
# Set the response header to the captured value if there was a match
Header set Access-Control-Allow-Origin %{ACCESS_CONTROL_ORIGIN}e env=ACCESS_CONTROL_ORIGIN
</IfModule>
</IfModule>
只需在块的顶部设置ACCESS_CONTROL_ROOT
变量为您的根域名,如果匹配您的域名,则它将在Access-Control-Allow-Origin:
响应头值中向客户端回显 Origin:
请求头值。
请注意,您可以使用sub.mydomain.example
作为ACCESS_CONTROL_ROOT
,它将限制来源为sub.mydomain.example
和*.sub.mydomain.example
(即它不必是域根)。可以通过修改正则表达式的URI匹配部分来控制允许变化的元素(协议、端口)。
我回答这个问题是因为被接受的答案无法实现以下功能:
例如:它不会为http://mywebsite.example
发送CORS标头,但可用于http://somedomain.mywebsite.example/
SetEnvIf Origin "http(s)?://(.+\.)?mywebsite\.com(:\d{1,5})?$" CORS=$0
Header set Access-Control-Allow-Origin "%{CORS}e" env=CORS
Header merge Vary "Origin"
为了启用您的网站,您只需在上面的 Apache 配置中将 mywebsite.example
替换为您自己的网站。
要允许多个站点:
SetEnvIf Origin "http(s)?://(.+\.)?(othersite\.com|mywebsite\.example)(:\d{1,5})?$" CORS=$0
部署后验证:
更改后,以下curl响应应该具有“Access-Control-Allow-Origin”头。
curl -X GET -H "Origin: http://site1.example" --verbose http://site2.example/query
iFrame
中使用 CORS 头绕过方法来获取一些内容...但它没有起作用。我猜我得使用 ajax
来获取这些内容。 - MeSo2.htaccess
文件。 - OfirD我需要一个仅使用PHP的解决方案,以防万一其他人也需要。它接受允许的输入字符串,例如“*.example.com”,如果输入匹配,则返回请求头服务器名称。
function getCORSHeaderOrigin($allowed, $input)
{
if ($allowed == '*') {
return '*';
}
$allowed = preg_quote($allowed, '/');
if (($wildcardPos = strpos($allowed, '*')) !== false) {
$allowed = str_replace('*', '(.*)', $allowed);
}
$regexp = '/^' . $allowed . '$/';
if (!preg_match($regexp, $input, $matches)) {
return 'none';
}
return $input;
}
这里是一个phpunit数据提供程序的测试用例:
// <description> <allowed> <input> <expected>
array('Allow Subdomain', 'www.example.com', 'www.example.com', 'www.example.com'),
array('Disallow wrong Subdomain', 'www.example.com', 'ws.example.com', 'none'),
array('Allow All', '*', 'ws.example.com', '*'),
array('Allow Subdomain Wildcard', '*.example.com', 'ws.example.com', 'ws.example.com'),
array('Disallow Wrong Subdomain no Wildcard', '*.example.com', 'example.com', 'none'),
array('Allow Double Subdomain for Wildcard', '*.example.com', 'a.b.example.com', 'a.b.example.com'),
array('Don\'t fall for incorrect position', '*.example.com', 'a.example.com.evil.com', 'none'),
array('Allow Subdomain in the middle', 'a.*.example.com', 'a.bc.example.com', 'a.bc.example.com'),
array('Disallow wrong Subdomain', 'a.*.example.com', 'b.bc.example.com', 'none'),
array('Correctly handle dots in allowed', 'example.com', 'exampleXcom', 'none'),
preg_quote()
,因为这是正确的方法(即使“.”是DNS名称中唯一有效的正则表达式元字符,preg_quote()
更能描述预期的操作)。 - DaveRandomreturn null;
可能更有意义,并且在这种情况下不应向客户端发送任何标题,因此调用者应该进行检查。 - DaveRandompreg_quote()
函数会对星号进行转义,因此例如str_replace()
的操作会导致出现孤立的反斜杠符号 ""。 - Christoffer Bubach在 .htaccess
中设置 Access-Control-Allow-Origin
,只有以下内容有效:
SetEnvIf Origin "http(s)?://(.+\.)?domain\.example(:\d{1,5})?$" CRS=$0
Header always set Access-Control-Allow-Origin "%{CRS}e" env=CRS
我尝试了几个其他建议的关键字Header append
, Header set
, 但这些都没有像Stack Overflow上许多答案中建议的那样工作,不过我不知道这些关键字是否已经过时或者对nginx无效。
以下是我的完整解决方案:
SetEnvIf Origin "http(s)?://(.+\.)?domain\.example(:\d{1,5})?$" CRS=$0
Header always set Access-Control-Allow-Origin "%{CRS}e" env=CRS
Header merge Vary "Origin"
Header always set Access-Control-Allow-Methods "GET, POST"
Header always set Access-Control-Allow-Headers: *
# Cached for a day
Header always set Access-Control-Max-Age: 86400
RewriteEngine On
# Respond with 200OK for OPTIONS
RewriteCond %{REQUEST_METHOD} OPTIONS
RewriteRule ^(.*)$ $1 [R=200,L]
对于 Spring Boot,我发现了这个 RegexCorsConfiguration
,它扩展了官方的 CorsConfiguration
:https://github.com/looorent/spring-security-jwt/blob/master/src/main/java/be/looorent/security/jwt/RegexCorsConfiguration.java
看起来原来的答案是针对 Apache 2.4 之前的版本的,对我没有用。以下是我所做的更改,使其在 2.4 版本中正常工作。这将适用于任何深度的 yourcompany.example
子域。
SetEnvIf Host ^((?:.+\.)*yourcompany\.example?)$ CORS_ALLOW_ORIGIN=$1
Header append Access-Control-Allow-Origin %{REQUEST_SCHEME}e://%{CORS_ALLOW_ORIGIN}e env=CORS_ALLOW_ORIGIN
Header merge Vary "Origin"
www.domain.example
)的字体时,遇到了与Font Awesome类似的问题,而这篇文章是我们的救星。请参见此处:如何解决“缺少跨源资源共享(CORS)响应头”Webfont问题?
对于复制/粘贴类型(并为一些贡献者致以敬意),我从所有的贡献中拼凑出了以下内容,并将其添加到站点根目录下的.htaccess
文件的顶部:<IfModule mod_headers.c>
<IfModule mod_rewrite.c>
SetEnvIf Origin "http(s)?://(.+\.)?(othersite\.example|mywebsite\.example)(:\d{1,5})?$" CORS=$0
Header set Access-Control-Allow-Origin "%{CORS}e" env=CORS
Header merge Vary "Origin"
</IfModule>
</IfModule>
超级安全,超级优雅。喜欢它:您无需将服务器带宽开放给资源窃贼/热链接者。
对于Spring,您应该使用allowedOriginPatterns
,它正好符合您的要求。
与setAllowedOrigins(java.util.List<java.lang.String>)相比,它支持更灵活的起源模式,其中在主机名中任何位置都可以使用“*”,并且还支持端口列表。 例如:
https://*.domain1.com -- domains ending with domain1.com
https://*.domain1.com:[8080,8081] -- domains ending with domain1.com on port 8080 or port 8081
https://*.domain1.com:[*] -- domains ending with domain1.com on any port, including the default port
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
List<String> allowedOrigins = List.of("https://*.example.com", "http://*.example2.com[80,8080]");
registry.addMapping("/**").allowedOriginPatterns(allowedOrigins.toArray(new String[0]));
}
};
}