Access-Control-Allow-Origin多个源域?

Tho*_*ley 985 .htaccess http xmlhttprequest cross-domain cors

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

我知道了*,但它太开放了.我真的想只允许一些域名.

举个例子,像这样:

Access-Control-Allow-Origin: http://domain1.example, http://domain2.example
Run Code Online (Sandbox Code Playgroud)

我已经尝试过上面的代码,但它似乎不适用于Firefox.

是否可以指定多个域,或者我只坚持一个?

小智 821

听起来像推荐的方法是让您的服务器从客户端读取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>
Run Code Online (Sandbox Code Playgroud)

  • 我对这个答案的问题是它并没有真正帮助我,因为我们使用CDN,显然我们无法控制CDN如何以编程方式设置标题. (148认同)
  • 如果需要关注缓存或CDN,请使用[Vary标头](http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.44)告知缓存/ CDN保留单独的响应不同的Origin请求标头值.你会在你的回复中包含一个像"Vary:Origin"这样的标题.缓存/ CDN然后知道它应该向具有标题"Origin:http://foo.example.com"的请求发送一个响应,并且对具有标题"Origin:http://bar.example"的请求发送不同的响应. .COM". (68认同)
  • 这符合W3C的建议 - http://www.w3.org/TR/cors/#access-control-allow-origin-response-hea (40认同)
  • @saturdayplace,如果你有权访问Origin标题,那么你已经过了CORS. (10认同)
  • 该正则表达式设计得不好;特别是,不应允许不安全的来源(使用“http”方案),并且应转义 DNS 标签分隔符(“\.”而不是“.”);否则,攻击者可以购买“developmentzgoogle.com”域名并从那里发起跨源攻击。 (9认同)
  • 对于任何想知道可以使用“(.+\.google.com)”代替“(google.com|staging.google.com)”的人 (8认同)
  • 我的答案中的实际示例(Nginx) - http://stackoverflow.com/a/12414239/6084 (6认同)
  • 是%{AccessControlAllowOrigin}`e`之后的`e`?[否](http://httpd.apache.org/docs/2.0/mod/mod_headers.html#header) (4认同)
  • 正如@BT所说,这不是一个很好的解决方案,因为它不适用于CDN.话虽如此,如果你仍然想要这样做,有两种典型的CDN配置.首先,您将服务器用作原始服务器.在这种情况下,指定一个简单的*(虽然不理想)将起作用.CDN会将此标头与您发送的任何其他标头一起缓存并将其提供给客户端.在第二种情况下,您使用远程(CDN托管)服务器作为源.在这种情况下,您应配置CDN以发回此内容的自定义HTTP标头.(再次,你指定*) (3认同)
  • 我认为SetEnvIf参数缺少转义点,即google.com应该是google.com,否则也可以使用googleFcom这样的域。 (3认同)
  • 如果没有匹配,这会如何表现?“Access-Control-Allow-Origin”的输出是什么? (3认同)
  • 这是客户端应该实现的Header限制...而不是服务器端IMHO实现的算法 (2认同)
  • 用这种方法,总是设置一个Vary标头以确保缓存不会破坏事物:`Header append Vary Origin`。 (2认同)

Nik*_*nov 209

我在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");
}
Run Code Online (Sandbox Code Playgroud)

  • 有`header('Access-Control-Allow-Origin:*')`有时候说如果凭证标志为真则不能使用通配符 - 当`header('Access-Control-Allow-Credentials:true')`可能时.因此,如果满足条件,最好是Allow-Origin the $ http_origin`本身 (14认同)
  • 为什么不使用http://stackoverflow.com/a/1850482/11635中建议的方法[并且不发送通配符,只是请求的来源]?如果没有更多的东西,这更宽松吗? (12认同)
  • @StephenR实际上"全封闭"会更准确,因为这样做的目的是将脚本打开到其他域;) (9认同)
  • 用`header("Access-Control-Allow-Origin:".$ http_origin)替换最后一行;`使它工作 (6认同)
  • HTTP_ORIGIN 不可靠,请参阅 /sf/ask/2886178151/ (5认同)
  • 该代码似乎有缺陷,因为如果未识别到HTTP_ORIGIN标头,则根本不会设置Access-Control-Allow-Origin,从而使脚本处于打开状态。 (2认同)

Jay*_*ave 108

这对我有用:

SetEnvIf Origin "^http(s)?://(.+\.)?(domain\.example|domain2\.example)$" origin_is=$0 
Header always set Access-Control-Allow-Origin %{origin_is}e env=origin_is
Run Code Online (Sandbox Code Playgroud)

放入时.htaccess,它肯定会起作用.

  • 对我来说是最好的解决方案,但我添加了端口支持(例如http:// localhost:3000用于开发):SetEnvIf Origin"^ http(s)?://(.+ \.)?(localhost | stackoverflow.com | example1 .com)(:[0-9] +)?$"origin_is = $ 0 (23认同)
  • 在stackoverflow的几个答案中,这是有效的. (2认同)

小智 88

我有与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>
Run Code Online (Sandbox Code Playgroud)

对于多个域,您只需更改正则表达式即可SetEnvIf.

  • 诀窍.只需确保正确调整正则表达式.我需要添加一个问号以允许域本身,例如``(.*\.?example\.org)`用于`example.com`和`sub.example.com`. (4认同)
  • 有关如何适应IIS 7的任何想法? (3认同)

mja*_*day 62

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

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

  • @Anoyz有一件事可能是增强的安全性,其中不允许"Allow*",但allow头的指定和匹配的主机名有效.这里的一个例子,如果你想跨域发送授权信息,你不能使用"允许*" (7认同)
  • example.org中的`.`是否被解释为任何值,因为这是一个正则表达式?在这种情况下,这会错误地允许自定义示例组织TLD吗? (3认同)

Rak*_*kib 26

这是我为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);
}
Run Code Online (Sandbox Code Playgroud)

如果我的服务器允许请求源,则将$http_origin其自身作为Access-Control-Allow-Origin标头的值返回,而不是返回*通配符.


eye*_*hUp 23

对于 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();
});
Run Code Online (Sandbox Code Playgroud)

  • 正是我正在寻找的东西。我几乎是对的:)你现在可以简化为“corsWhiteList.includes(req.headers.origin)” (2认同)

小智 20

您应该注意一个缺点:只要您将文件外包到CDN(或任何其他不允许编写脚本的服务器)或者您的文件缓存在代理上,就会根据"Origin"更改响应请求标头将无法正常工作.

  • 如果需要关注缓存或CDN,请使用Vary标头告诉缓存/ CDN为不同的Origin请求标头值保留单独的响应.你会在你的回复中包含一个像"Vary:Origin"这样的标题.缓存/ CDN然后知道它应该向具有标题"Origin:http://foo.example.com"的请求发送一个响应,并且对具有标题"Origin:http://bar.example"的请求发送不同的响应. .COM". (12认同)
  • 你能详细说明一下,或者指出我们可以寻找更多信息吗?我希望用Limelight做到这一点,我希望你错了.我们的一位技术人员说,只要我们的CDN种子服务器发送标头,CDN本身就会发送它.尚未测试出来 (4认同)

Geo*_*rge 19

对于多个域,在您的.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>
Run Code Online (Sandbox Code Playgroud)

  • 这个片段对我来说非常适合.但我不明白它的作用:D (4认同)
  • 这对我有用,虽然我不得不添加'^'即... SetEnvIf Origin"^ http(s)?://(www \.)? (2认同)
  • 我不得不将`AccessControlAllowOrigin = $ 0 $ 1`更改为`AccessControlAllowOrigin = $ 0`。否则,它不适用于HTTPS来源。`http:// example.com`正确地出现了,但是`https:// example.com`以`https:// example.coms`出现了,最后还有一个额外的`s`。 (2认同)

Adr*_*osa 17

对于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";
   }
}
Run Code Online (Sandbox Code Playgroud)

这将只回显与给定的域列表匹配的"Access-Control-Allow-Origin"标头.


Pac*_*ate 15

对于安装了URL Rewrite 2.0模块的IIS 7.5+,请参阅此SO答案


duv*_*uvo 13

这是基于yesthatguy的答案的Java Web应用程序解决方案.

我正在使用Jersey REST 1.x.

配置web.xml以了解Jersey 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>
Run Code Online (Sandbox Code Playgroud)

这是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;
}

}
Run Code Online (Sandbox Code Playgroud)


her*_*vnc 11

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

我的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';
}
Run Code Online (Sandbox Code Playgroud)


drA*_*erT 9

也许我错了,但据我所知,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
Run Code Online (Sandbox Code Playgroud)

从这一点来说,我认为不同的起源被承认,应该是空间分离.

  • [CORS规范](http://www.w3.org/TR/cors/#access-control-allow-origin-response-header)部分"5.1访问控制允许原点响应标题"表明原点 - list受约束:_Rather比允许以空格分隔的原点列表,它是单个原点或字符串"null"._ (7认同)
  • 这似乎是对规范的正确解释; 也就是说,当前的浏览器似乎没有完全支持该规范(例如,我刚刚在Firefox 17.0上测试过它,并确认它不起作用). (2认同)
  • 正如我在对自己的回答的评论中提到的那样,这是实现者注释的一部分,而不是RFC 2119的要求.'正确'的答案绝对是使用空格分隔的值.问题只是实现不完整,因此"正确"的答案不一定有效.它应该,但它没有.但是,在将来,随着实施变得更好,这可能会改变. (2认同)

Ale*_*x W 7

我努力为运行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>
Run Code Online (Sandbox Code Playgroud)

更改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
Run Code Online (Sandbox Code Playgroud)


nou*_*oun 5

如果您在字体方面遇到麻烦,请使用:

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


Lia*_*kos 5

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');
Run Code Online (Sandbox Code Playgroud)


归档时间:

查看次数:

737991 次

最近记录:

5 年,11 月 前