如何使用CORS Pre-flight请求处理自定义标头? AJAX - CodeIgniter

时间:2022-10-06 23:02:19

I'm working with CodeIgniter and the Restfull API to structure my web server private API.

我正在使用CodeIgniter和Restfull API来构建我的Web服务器私有API。

I've started using CORS as per requirements for some framework I'm using.

我已经开始根据我正在使用的一些框架的要求使用CORS。

Working with Jquery, I can see 2 requests are sent, first one as OPTION type - as expected - but without my custom header (X-API-KEY for security, by default in CodeIgniter Restful API).

使用Jquery,我可以看到发送了2个请求,第一个是OPTION类型 - 正如预期的那样 - 但没有我的自定义标头(X-API-KEY用于安全性,默认情况下在CodeIgniter Restful API中)。

I then receive an invalid API Key error message as shown in the picture. Then right after the proper request is being sent with correct headers but in the meantime, the first requests triggered the .fail() function to handle errors.

然后,我收到无效的API密钥错误消息,如图所示。然后在正确的标头发送正确的请求之后,同时,第一个请求触发.fail()函数来处理错误。

如何使用CORS Pre-flight请求处理自定义标头? AJAX  -  CodeIgniter如何使用CORS Pre-flight请求处理自定义标头? AJAX  -  CodeIgniter

What's best practice to handle that scenario ? i would like my ajax request to smoothly handle the first preflight OPTION request without triggering an error on my app as it does today, then do the normal GET call with custom headers as per how CORS works and execute the success call without never triggering the error call in the first preflight request ?

处理这种情况的最佳做法是什么?我希望我的ajax请求能够顺利处理第一个预检OPTION请求,而不会像我今天那样在我的应用程序上触发错误,然后根据CORS的工作方式使用自定义标头进行正常的GET调用并执行成功调用,而不会触发错误在第一次预检请求中打电话?

triggerFriendsWall: function() {
        //Get location
            var options = {
                timeout: 30000,
                enableHighAccuracy: true,
                maximumAge: 90000
            };

            //We need to check if user has disabled geolocation, in which case it makes the app crashes ! (from Cordova.js 1097)
            var position = JSON.parse(localStorage.getItem("position"));

            if (position == "" || position == null || position == "null" || typeof position == "undefined" ) {
                // In this case we have never set location to anything and the user has enabled it.
                navigator.geolocation.getCurrentPosition( function(position) {
                    home.friendsWall(position);
                }, function(error) {
                    common.handle_errors(error, home.friendsWall);
                }, options);
            } else {
                // in this case, user has disabled geolocatoin !
                common.handle_errors(false, home.friendsWall);
            }
},


friendsWall: function(position) {

    $.when(UserDAO.getUsersNearby(position.coords.latitude, position.coords.longitude, home.Usr_radius, home.Usr_limit, home.Usr_offset))
                .done(function(response) {
                   // Do stuff
    })
}


getUsersNearby: function(lat, lng, radius, limit, offset) {
            var key = localStorage.getItem("key");

            return $.ajax({
                type: "GET",
                url: config.server_url + 'user/usersSurrounding',
                headers: {
                    'X-API-KEY': key
                },
                data: {
                    lat: lat,
                    lng: lng,
                    radius: radius,
                    limit: limit,
                    offset: offset
                },
                dataType: 'json'
            });
        },

Many thanks

非常感谢

EDIT: This is the constructor associated to all my controllers ( all controller extend a single controller where construct method is : )

编辑:这是与我的所有控制器关联的构造函数(所有控制器扩展单个控制器,其中构造方法是:)

public function __construct()
{

    header('Access-Control-Allow-Origin: *');
    header("Access-Control-Allow-Headers: X-API-KEY, Origin, X-Requested-With, Content-Type, Accept, Access-Control-Request-Method");
    header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
    $method = $_SERVER['REQUEST_METHOD'];
    if($method == "OPTIONS") {
        die();
    }

    parent::__construct();
    // $this->load->model('admin_model');
    $this->load->library('session');
    $this->load->library('key');
}

2 个解决方案

#1


1  

Are you using Access-Control-Allow-Headers?

您使用的是Access-Control-Allow-Headers吗?

Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.

用于响应预检请求,以指示在发出实际请求时可以使用哪些HTTP标头。

Try adding the following header to your preflight code.

尝试将以下标题添加到预检代码中。

header("Access-Control-Allow-Headers: content-type, origin, accept, X-API-KEY");

I recall having similar issues, seem to recall some of it being browser specific too...

我记得有类似的问题,似乎还记得其中一些也是浏览器特定的......

If it helps here is a snippet from some code I know works:

如果它有帮助,这里是我知道的一些代码的片段:

// CORS and other headers.  Make sure file is not cached (as it happens for example on iOS devices)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Max-Age: ' . CORS_AUTH_MAX_AGE);

//CORS preflight
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

    header("Access-Control-Allow-Headers: content-type, origin, accept, x-app-sig");

    $acrh = explode(',', strtolower($headers['Access-Control-Request-Headers']));
    foreach ($acrh as $k => $v) {
        $acrh[$k] = trim($v);
    }

    if (! isset($headers['Access-Control-Request-Headers']) || ! in_array('x-app-sig', $acrh)) {
        _log($h, '*** Bad preflight!' . PHP_EOL . print_r($headers, true) . PHP_EOL . print_r($_REQUEST, true));
        header("HTTP/1.1 401 Unauthorized");
        exit; //->
    }

    _log($h, '+++ Successful preflight.' . PHP_EOL . print_r($headers, true) . PHP_EOL . print_r($_REQUEST, true));
    exit; //->
}

//Now we are past preflight.  Actual Auth happens here, I check a signature that I post with payload.

Update: OK, think I better understand your question now. Posted a bit more code. First off, yes, we are doing essentially the same thing there. I just check that the preflight tried to white-list what it should have in terms of headers.

更新:好的,我想我现在更好地理解你的问题。发布了更多代码。首先,是的,我们在那里做的基本相同。我只是检查一下预检试图按标题列出它应该具有的内容。

I think the part you are missing is that the preflight should/will not have the custom header you are trying to send. See the answer here: How do you send a custom header in a cross-domain (CORS) XMLHttpRequest?). So like I do you could check that the Access-Control-Request-Headers: are sent with the preflight, but you should not check for the actual header being present on that call.

我认为您缺少的部分是预检应该/不会有您尝试发送的自定义标题。请参阅此处的答案:如何在跨域(CORS)XMLHttpRequest中发送自定义标头?)。就像我一样,您可以检查Access-Control-Request-Headers:是否与预检一起发送,但您不应检查该呼叫上是否存在实际的标头。

Sounds like you just need to move a little of the code around server side - make the preflight pretty vanilla and dumb, then do your actual auth or checking of custom headers after successful preflight.

听起来你只需要在服务器端移动一些代码 - 使预检非常香草和愚蠢,然后在成功预检后进行实际验证或检查自定义标题。

I use a HMAC signature sent with the payload myself to authenticate things after the preflight. I also check that the custom x-app-sig is supplied and what i expect though that is probably redundant.

我使用自带有效负载的HMAC签名来验证预检后的事情。我还检查了自定义x-app-sig是否已经提供以及我期望的那些可能是多余的。

#2


0  

I have been dueling with this issue for two days. It comes out, in the end, there must be a flux for this requisitions and it is quit simple. First you have to allow the header fields that are being sent (with all the CORS headers), in my case it was:

我已经和这个问题决斗了两天。最终,这个请购单必须有一个流量,并且它很简单。首先,您必须允许发送的头字段(包含所有CORS头),在我的情况下,它是:

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: authorization, content-type, x-requested-with');

Then I simply had to return status "204 No Content" whenever an request OPTIONS method came. You can use a condition statement like so:

然后,每当请求OPTIONS方法到来时,我只需返回状态“204 No Content”。你可以像这样使用条件语句:

if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    header('HTTP/1.1 204 No Content');
}

#1


1  

Are you using Access-Control-Allow-Headers?

您使用的是Access-Control-Allow-Headers吗?

Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request.

用于响应预检请求,以指示在发出实际请求时可以使用哪些HTTP标头。

Try adding the following header to your preflight code.

尝试将以下标题添加到预检代码中。

header("Access-Control-Allow-Headers: content-type, origin, accept, X-API-KEY");

I recall having similar issues, seem to recall some of it being browser specific too...

我记得有类似的问题,似乎还记得其中一些也是浏览器特定的......

If it helps here is a snippet from some code I know works:

如果它有帮助,这里是我知道的一些代码的片段:

// CORS and other headers.  Make sure file is not cached (as it happens for example on iOS devices)
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST');
header('Access-Control-Max-Age: ' . CORS_AUTH_MAX_AGE);

//CORS preflight
if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] == 'OPTIONS') {

    header("Access-Control-Allow-Headers: content-type, origin, accept, x-app-sig");

    $acrh = explode(',', strtolower($headers['Access-Control-Request-Headers']));
    foreach ($acrh as $k => $v) {
        $acrh[$k] = trim($v);
    }

    if (! isset($headers['Access-Control-Request-Headers']) || ! in_array('x-app-sig', $acrh)) {
        _log($h, '*** Bad preflight!' . PHP_EOL . print_r($headers, true) . PHP_EOL . print_r($_REQUEST, true));
        header("HTTP/1.1 401 Unauthorized");
        exit; //->
    }

    _log($h, '+++ Successful preflight.' . PHP_EOL . print_r($headers, true) . PHP_EOL . print_r($_REQUEST, true));
    exit; //->
}

//Now we are past preflight.  Actual Auth happens here, I check a signature that I post with payload.

Update: OK, think I better understand your question now. Posted a bit more code. First off, yes, we are doing essentially the same thing there. I just check that the preflight tried to white-list what it should have in terms of headers.

更新:好的,我想我现在更好地理解你的问题。发布了更多代码。首先,是的,我们在那里做的基本相同。我只是检查一下预检试图按标题列出它应该具有的内容。

I think the part you are missing is that the preflight should/will not have the custom header you are trying to send. See the answer here: How do you send a custom header in a cross-domain (CORS) XMLHttpRequest?). So like I do you could check that the Access-Control-Request-Headers: are sent with the preflight, but you should not check for the actual header being present on that call.

我认为您缺少的部分是预检应该/不会有您尝试发送的自定义标题。请参阅此处的答案:如何在跨域(CORS)XMLHttpRequest中发送自定义标头?)。就像我一样,您可以检查Access-Control-Request-Headers:是否与预检一起发送,但您不应检查该呼叫上是否存在实际的标头。

Sounds like you just need to move a little of the code around server side - make the preflight pretty vanilla and dumb, then do your actual auth or checking of custom headers after successful preflight.

听起来你只需要在服务器端移动一些代码 - 使预检非常香草和愚蠢,然后在成功预检后进行实际验证或检查自定义标题。

I use a HMAC signature sent with the payload myself to authenticate things after the preflight. I also check that the custom x-app-sig is supplied and what i expect though that is probably redundant.

我使用自带有效负载的HMAC签名来验证预检后的事情。我还检查了自定义x-app-sig是否已经提供以及我期望的那些可能是多余的。

#2


0  

I have been dueling with this issue for two days. It comes out, in the end, there must be a flux for this requisitions and it is quit simple. First you have to allow the header fields that are being sent (with all the CORS headers), in my case it was:

我已经和这个问题决斗了两天。最终,这个请购单必须有一个流量,并且它很简单。首先,您必须允许发送的头字段(包含所有CORS头),在我的情况下,它是:

<?php
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
header('Access-Control-Allow-Headers: authorization, content-type, x-requested-with');

Then I simply had to return status "204 No Content" whenever an request OPTIONS method came. You can use a condition statement like so:

然后,每当请求OPTIONS方法到来时,我只需返回状态“204 No Content”。你可以像这样使用条件语句:

if (isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    header('HTTP/1.1 204 No Content');
}