如何使用CORS Pre-flight请求处理自定义标头? AJAX – 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' }); }, 

非常感谢

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

 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'); } 

您使用的是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 MYH: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. 

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

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

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

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

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

  

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

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