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

Mil*_* M. 6 javascript php ajax jquery codeigniter

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

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

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

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

First Pre Flight调用触发无效的API KEY,因为我们无法在此调用中传递X-API-KEY自定义标头 正常通话工作正常

处理该场景的最佳做法是什么?我希望我的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'
            });
        },
Run Code Online (Sandbox Code Playgroud)

非常感谢

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

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

fic*_*scr 3

您是否使用Access-Control-Allow-Headers

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

尝试将以下标头添加到您的预检代码中。

header("Access-Control-Allow-Headers: content-type, origin, accept, X-API-KEY");
Run Code Online (Sandbox Code Playgroud)

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

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

// 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.
Run Code Online (Sandbox Code Playgroud)

更新:好的,我想我现在更好地理解你的问题了。发布了更多代码。首先,是的,我们正在做基本上相同的事情。我只是检查预检是否尝试将其在标头方面应具有的内容列入白名单。

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

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

我自己使用与有效负载一起发送的 HMAC 签名来在预检后进行身份验证。我还检查是否提供了自定义 x-app-sig 以及我所期望的内容,尽管这可能是多余的。