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

时间:2015-06-18 16:03:56

标签: javascript php jquery ajax codeigniter

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

我已根据我使用的某个框架的要求开始使用CORS。

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

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

First Pre Flight call trigger invalid API KEY since we can't pass the X-API-KEY custom header in this call The normal call working fine

处理该方案的最佳做法是什么?我希望我的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'
            });
        },

非常感谢

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

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 个答案:

答案 0 :(得分:3)

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

  

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

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

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

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

如果它有帮助,这里有一些我认识的代码片段:

// 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.

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

我认为您缺少的部分是预检应该/不会有您尝试发送的自定义标题。请在此处查看答案:How do you send a custom header in a cross-domain (CORS) XMLHttpRequest?)。就像我一样,您可以检查Access-Control-Request-Headers:是否与预检一起发送,但是您不应该检查该呼叫上是否存在实际的标头。

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

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

答案 1 :(得分:0)

我已经和这个问题决斗了两天。最终,这个请购单必须有一个变化,并且它很简单。首先,您必须允许发送的头字段(包含所有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');

然后,只要请求OPTIONS方法到来,我就必须返回状态“204 No Content”。您可以使用如下条件语句:

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