Я пытаюсь понять, как CORS Filter работает и как можно извлечь из него выгоду, особенно когда мы говорим о опции Origin
.
IЯ работаю над проектом, где мне нужно предоставить клиенту доступ для добавления потенциальных клиентов в API с помощью запросов Ajax.
Я использую yii\rest\Controller
для моего контроллера.У меня уже есть API, и он работает, у меня есть базовый контроллер, где я устанавливаю свое поведение и другие общие методы и расширяю все свои контроллеры из этого базового контроллера.
Код для базового контроллера ниже
class ApiController extends yii\rest\Controller
{
/**
* @var array
*/
protected $_logs = [];
/**
* Implemented CORS and HTTP Basic Auth for API
*
* @return string
*/
public function behaviors()
{
$behaviors = parent::behaviors();
// remove authentication filter
$auth = $behaviors['authenticator'];
// remove authentication filter necessary because we need to
// add CORS filter and it should be added after the CORS
unset($behaviors['authenticator']);
// add CORS filter
$behaviors['corsFilter'] = [
'class' => Cors::class,
'cors' => [
'Origin' => ['http://www.my-sms-business.local', 'http://localhost'],
'Access-Control-Request-Method' => ['GET', 'POST'],
],
];
// re-add authentication filter
$behaviors['authenticator'] = $auth;
// avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
$behaviors['authenticator']['except'] = ['options'];
return $behaviors;
}
}
Как вы видите, у меня есть CORS, определенный с http://localhost
, добавленным в Origin
, теперь, согласно Yii, он должен позволятьзапросы от этих доменов, и он должен работать без сбоев. Да, он работает правильно, когда я использую простую HTML-страницу с формой и кодом javascript для передачи ссылки на API, код выглядит следующим образом.
$(document).ready(function() {
$("#my-form").submit(function(e) {
e.preventDefault();
//serialize form input data
let data = $(this).serialize();
//get the form action
let url = $(this).attr('action');
$.ajax({
url: url,
method: 'POST',
data: data,
success: function(data) {
if (data.status == 200) {
alert(data.message);
} else {
alert("An error occoured see details,\n " + data.message.join(
"\n"));
}
},
})
return false;
});
});
<form name="my-form" id="my-form" method="POST" action="http://www.my-sms-business.local/api/v1/lead/add?access-token=MY-ACCESS-TOKEN">
<label>Phone</label>
<input type="text" name="phone_number" id="phone_number" />
<label>Campaign Id</label>
<input type="text" name="campaign_id" />
<label>user ID</label>
<input type="text" name="user_id" />
<label>name</label>
<input type="text" name="name" />
<input type="submit" value="Submit" />
</form>
LeadController
использует QueryParamAuth
при отправке access-token
через строку запроса в URL-адресе вызова ajax.
class LeadController extends ApiController
{
/**
* @return mixed
*/
public function behaviors()
{
$behaviors = parent::behaviors();
$behaviors['authenticator'] = [
'class' => QueryParamAuth::class,
];
return $behaviors;
}
/**
* Add the subscriber to the campaign
*
* @return mixed
*/
public function actionAdd()
{
if (Yii::$app->request->isPost) {
//start db transaction
$transaction = Yii::$app->db->beginTransaction();
//post data
$post['SubscribersForm'] = Yii::$app->request->post();
$model = new SubscribersForm();
$model->active = SubscribersForm::ACTIVE_YES;
$model->type = SubscribersForm::TYPE_SINGLE;
try {
if ($model->load($post)) {
if ($model->validate()) {
//if subscriber already exists in the database
$subscriberExists = Subscriber::findOne(['phone_number' => $model->phone_number]) !== null;
// call add subscriber so that the subscriber is added to the
// specified campaign and also to other campaigns under the
// same number the current campaign is associated to.
$model->saveSubscribers();
//commit the transaction
$transaction->commit();
if ($subscriberExists) {
return $this->formatMessage(['The Phone exsts in our database, we have added the number to the campaign that was provided.'], 200);
} else {
return $this->formatMessage(['Lead added successfully'], 200);
}
} else {
return $this->formatMessage($model->getErrorSummary(true), 401);
}
}
} catch (Exception $ex) {
//roll back the transaction
$transaction->rollBack();
//return the error response
return $this->formatMessage([$ex->getMessage()], $ex->getCode());
}
}
}
}
Теперь я хотел проверить, что если я удаляю http://localhost
из Origin
, он не должен разрешать запрос от этого источника.Когда я удаляю его и нажимаю кнопку «Отправить» в форме для отправки вызова AJAX, я вижу, что консоль выдает предупреждение
Запрос перекрестного источника заблокирован: та же политика происхождения запрещает чтение удаленного ресурсав http://www.my -sms-business.local / api / v1 / lead / add? access-token = MY-ACCESS-TOKEN .(Причина: отсутствует заголовок CORS «Access-Control-Allow-Origin»).
И предупреждения, которые находятся внутри успешного обратного вызова вызова ajax, не срабатывают, пока что это хорошо.
НО, когда я нажимаю на сгенерированный запрос и просматриваю детали BOOM , он все еще идет вперед к actionAdd()
в LeadController
и пытается добавить отведение?См. Изображение ниже
Это как это работает или работает?Я так не думаю, потому что я думал, что если разрешенный домен отсутствует в Origin
, запрос будет прерван.В настоящее время то, как он себя ведет, становится таким способом бесполезным.
ТАК где я тут не так делаю?Какой конфиг мне не хватает.Мы планируем предоставить опцию во внешнем интерфейсе сайта, где пользователи могут создавать свои ключи API и добавлять свои домены в белый список, откуда они будут отправлять запросы на добавление потенциальных клиентов.
ОБНОВЛЕНИЕ
К моему удивлению, если я использую HttpBasicAuth
и добавляю заголовки через мой вызов ajax, используя beforeSend
, как показано ниже
beforeSend: function (xhr) {
xhr.setRequestHeader("Authorization",
"Basic " + btoa('MY_ACCESS_TOKEN:'));
xhr.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
},
, и удаляю QueryParamAuth
из моего LeadController
.И обновите метод behaviors()
в ApiController
до следующего
public function behaviors()
{
$behaviors = parent::behaviors();
// remove authentication filter
$auth = $behaviors['authenticator'];
// remove authentication filter necessary because we need to
// add CORS filter and it should be added after the CORS
unset($behaviors['authenticator']);
// add CORS filter
$behaviors['corsFilter'] = [
'class' => Cors::class,
'cors' => [
'Origin' => ['http://www.my-sms-business.local', 'http://localhost'],
'Access-Control-Request-Method' => ['GET', 'POST', 'OPTIONS'],
'Access-Control-Request-Headers' => ['Authorization'],
'Access-Control-Allow-Credentials' => true,
],
];
// re-add authentication filter
$behaviors['authenticator'] = [
'class' => HttpBasicAuth::class,
];
// avoid authentication on CORS-pre-flight requests (HTTP OPTIONS method)
$behaviors['authenticator']['except'] = ['OPTIONS'];
return $behaviors;
}
. Он начинает работать, как и ожидалось, если я удаляю домен из белого списка или Origin, он не добавляет лид, как OPTIONS
запрос отправляется до того, как будет отправлен фактический запрос, и при сбое он не инициирует запрос POST
.
Итак, что же все это значит, почему он переходит к действию, даже если домен нев белый список при использовании QueryParamAuth
?.