使用访问控制源指定多个子域

时间:2012-03-09 08:46:24

标签: php javascript ajax header cross-domain

我正在尝试允许访问我网站上的每个子域,以允许跨子域AJAX调用。有没有办法指定网站的所有子域,如*.example.com,或者,当我列出多个域时,为什么以下内容不起作用:

header('Access-Control-Allow-Origin: http://api.example.com http://www.example.com');

我已经阅读了以下似乎相似的问题,如果不是与此问题相同,除了我想要访问子域这一事实,而且这个问题涉及一般域。

Access-Control-Allow-Origin Multiple Origin Domains?

如果上述问题是此问题的解决方案,那么我如何从标题中检索原点。似乎$ _SERVER ['HTTP_ORIGIN']非常不可靠,甚至不能跨浏览器。我需要能够在尝试使用javascript发送AJAX调用时在任何可能显示错误的浏览器中看到原点。

8 个答案:

答案 0 :(得分:17)

此问题的解决方案是使用$ _SERVER ['HTTP_ORIGIN']变量来确定请求是否来自允许的域。然后它设置了这个:

header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);

答案 1 :(得分:10)

这是我的表现。

Origin标头由浏览器指定,并包含在其他域上请求脚本的域:

Origin: http://www.websiteA.com

因此,你可以"白名单"服务器端脚本中的多个域:

$allowedOrigins = [
    "http://www.websiteA.com",
    "https://www.websiteB.com"
    // ... etc
];

您可以执行的操作是检查$_SERVER["HTTP_ORIGIN"]全局是否包含该白名单中的域:

if (in_array($_SERVER["HTTP_ORIGIN"], $allowedOrigins)) {

并将Access-Control-Allow-Origin响应标头设置为Origin标头值为:

header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);

完整脚本:

$allowedOrigins = [
    "http://www.websiteA.com",
    "https://www.websiteB.com"
    // ... etc
];

if (in_array($_SERVER["HTTP_ORIGIN"], $allowedOrigins)) {
    header("Access-Control-Allow-Origin: " . $_SERVER["HTTP_ORIGIN"]);
}

答案 2 :(得分:3)

虽然答案有效,但它确实会破坏整个事物的目的,因为它允许来自任何主机的请求。

我用的是:

if(isset($_SERVER['HTTP_ORIGIN'])) {
  $origin = $_SERVER['HTTP_ORIGIN'];
  if($origin == 'https://sub1.my-website.com' OR $origin == 'https://sub2.my-website.com') {
    header("Access-Control-Allow-Origin: $origin");
  }
}

答案 3 :(得分:0)

我尝试使用这种方法来实现特定域的约束:

$allowed_origin = '';
$parts = explode('.', parse_url($_SERVER['HTTP_HOST'])['host']);
if(end($parts).".".prev($parts) === "com.domain") {
    $allowed_origin = $_SERVER['HTTP_ORIGIN'];
    header('Acesss-Control-Allow-Origin: '. $allowed_origin);
}

我希望它有效。

答案 4 :(得分:0)

如果您想要通配符域,我认为这会更有效

if(isset($_SERVER['HTTP_ORIGIN']) && preg_match('!^http(s)?://([a-z0-9\-]+\.)?example.com$!is', $_SERVER['HTTP_ORIGIN']))
{
    header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
}

答案 5 :(得分:0)

//Function to be called first in php file.
function CORS_HEADERS_HANDLER(){
  if (isset($_SERVER['HTTP_ORIGIN'])){
    switch($_SERVER['HTTP_ORIGIN']){
        //Handle an IP address and Port
      case 'http://1.2.3.4:4200':
        header('Access-Control-Allow-Origin: http://1.2.3.4:4200');
        break;
        //Handle an Website Domain (using https)
      case 'https://www.someSite.com':
        header('Access-Control-Allow-Origin: https://www.someSite.com');
        break;
        //Handle an Website Domain (using http)
      case 'http://www.someSite.com':
        header('Access-Control-Allow-Origin: http://www.someSite.com');
        break;
        //Catch if someone's site is actually the reject being cheeky
      case 'https://not.you':
        header('Access-Control-Allow-Origin: https://nice.try');
        break;
        //Handle a rejection passing something that is not the request origin.
      default:
        header('Access-Control-Allow-Origin: https://not.you');
        break;
    }
  }else{
    header('Access-Control-Allow-Origin: https://not.you');
  }
  header('Access-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE, OPTIONS');
  header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');
  header('Access-Control-Allow-Credentials: true');
  header('Content-Type: application/json; charset=utf-8');
  header("Cache-Control: public,max-age=3600");
  //if its an options request you don't need to proceed past CORS request.
  if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    die();
  }
}

答案 6 :(得分:0)

这是我的挑战和解决方案:

1 - api.example.com 上的后端 PHP。

2 - 多个 JS 前端,例如 one.example.com、two.example.com 等

3 - Cookie 需要双向传递。

4 - api.example.com 上从多个前端到 PHP 后端的 AJAX 调用

5 - 在 PHP 中,我不喜欢使用 $_SERVER["HTTP_ORIGIN"],在我看来并不总是可靠/安全(我有一些浏览器的 HTTP-ORIGIN 总是空的)。

在具有单个前端域的 PHP 中执行此操作的正常方法是使用以下内容启动 PHP 代码:

header('Access-Control-Allow-Origin: https://one.example.com');  
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');  
header('Access-Control-Allow-Credentials: true');  

在 one.example.com 域上的 JS 中:

jQuery.ajax({
    url: myURL,
    type: "POST",
    xhrFields: {withCredentials: true},
    dataType: "text",
    contentType: "text/xml; charset=\"utf-8\"",
    cache: false,
    headers: "",
    data: myCallJSONStr,
    success: function(myResponse) {.....}

但是,这是行不通的,因为我使用多个子域来调用我的 API 域。

并且此解决方案将不起作用,因为我想传递 cookie:

header('Access-Control-Allow-Origin: *');  

与JS站点上的pass on cookie设置冲突:

xhrFields: {withCredentials: true}

这是我所做的:

1 - 使用 GET 参数传递子域。 2 - 在 PHP 中硬编码主域,因此只允许(所有)子域。

这是我的解决方案的 JS/JQuery AJAX 部分:

function getSubDomain(){
    
    let mySubDomain = "";
    
    let myDomain = window.location.host;
    let myArrayParts = myDomain.split(".");
    if (myArrayParts.length == 3){
        mySubDomain = myArrayParts[0];
    }
    
    return mySubDomain;
    
}

在 AJAX 调用中:

    let mySubDomain = getSubDomain();
    if (mySubDomain != ""){
        myURL += "?source=" + mySubDomain + "&end"; //use & instead of ? if URL already has GET parameters
    }
    
    jQuery.ajax({
        url: myURL,
        type: "POST",
        xhrFields: {withCredentials: true},
        dataType: "text",
        contentType: "text/xml; charset=\"utf-8\"",
        cache: false,
        headers: "",
        data: myCallJSONStr,
        success: function(myResponse) {.....}

最后是 PHP 部分:

<?php

$myDomain = "example.com";
$mySubdomain = "";

if (isset($_GET["source"])) {
    $mySubdomain = $_GET["source"].".";
}

$myDomainAllowOrigin = "https://".$mySubdomain.$myDomain;
$myAllowOrigin = "Access-Control-Allow-Origin: ".$myDomainAllowOrigin;

//echo $myAllowOrigin;

header($myAllowOrigin);  
header('Access-Control-Allow-Headers: Origin, Content-Type, X-Auth-Token');  
header('Access-Control-Allow-Credentials: true');

重要,不要忘记为所有子域设置 cookie,在这种情况下,cookie 的域将是:.example.com(因此在主域前面有一个点):

<?php

    //////////////// GLOBALS /////////////////////////////////
    
    $gCookieDomain = ".example.com";
    $gCookieValidForDays = 90;
    
    //////////////// COOKIE FUNTIONS /////////////////////////////////
    
    function setAPCookie($myCookieName, $myCookieValue, $myHttponly){
        global $gCookieDomain;
        global $gCookieValidForDays;
        
        $myExpires = time()+60*60*24*$gCookieValidForDays;
        setcookie($myCookieName, $myCookieValue, $myExpires, "/", $gCookieDomain, true, $myHttponly);   
        
        return $myExpires;
    }

此解决方案允许我从 example.com 上的任何子域调用 api.example.com 上的 API。

注意。对于只有一个调用子域的情况,我更喜欢使用 .htaccess 来设置 CORS 而不是 PHP。下面是一个 .htaccess (linux/apache) 的例子,只有 one.example.com 调用 api.example.com:

<IfModule mod_headers.c>
    Header set Access-Control-Allow-Origin "https://one.example.com"
    Header set Access-Control-Allow-Headers "Origin, Content-Type, X-Auth-Token"
    Header set Access-Control-Allow-Credentials "true"
</IfModule>

并将这个 .htaccess 放在 api.example.com 的根目录中。

答案 7 :(得分:-3)

这是关于你的问题的好文章: http://hacks.mozilla.org/2009/07/cross-site-xmlhttprequest-with-cors/

如果这不够好或不起作用,你需要这样的代理: https://raw.github.com/cowboy/php-simple-proxy/master/ba-simple-proxy.php

然后您可以使用以下参数调用代理: http://www.example.com/ba-simple-proxy.php?url=https://api.example.com