Содержание
Простой случай
Как правило проблема с доступом к API OpenCart возникает когда не настроен доступ по API.
Для решения этой проблемы нужно пройти в админке в Система-Пользователи-API
, зайти в нужный объект списка и добавить свой IP адрес в список.
Либо на странице, с сообщением о проблеме с API просто нажать на кнопку Добавить IP-адрес
и обновить страницу.
Но у нашего клиента на OpenCart 2.3 было не все так просто ...
Случай клиента
Перед началом разработки модуля клиент сообщил мне, что в админке на странице редактирования заказа у него часто/рандомно не работает изменение заказа: У вас нет разрешения на доступ к API!
А как позже выяснилось, такая проблема у клиента на 2 сайтах, где используется одна и та же версия OpenCart 2.3, сайты размещены у одного и того же хостера.
Изначально я не придал этому значения, так как проблема у клиента была до меня, а сроки по разработке были крайне сжатые (как обычно), а значит ее решение это второстепенная задача, однако на этапе внедрения модуля в интернет-магазин клиента начались проблемы.
Дело в том, что интерфейс функциональности разработанного мной модуля располагался на странице редактирования заказа, во вкладке Товары
, а из-за проблем доступа к API клиент не мог проверить работу модуля. Но это была первая часть проблемы. Как выяснилось позже мой модуль не мог корректно функционировать, так как использовал доступ к заказу основываясь на API.
Стоит уточнить, что интерфейс редактирования заказа во многом построен на Ajax API OpenCart, с использованием авторизации и получением токена для доступа к API.
Первым делом я перепроверил ajax запросы на корретность работы, как на клиенте так и на сервере. Токен на клиенте есть, проверка токена и авторизация по токену на сервере есть. Все также как и у API запросов, но не работает ...
В чем проблема?
В другой статье мы уже вкратце разбирали Ajax API, а теперь копнем глубже.
Посмотрим контроллер catalog/controller/api/login.php
(запрос получения токена для работы с API /index.php?route=api/login
), в случае валидного API key
и наличия в этой группе IP адреса выполняющего (того кто делает запрос), данный запрос стартует новую сессию с именем api
с единственным ключом api_id
:
$session_id_new = $this->session->createId();
$this->session->start('api', $session_id_new);
$this->session->data['api_id'] = $api_info['api_id'];
Затем посмотрим catalog/controller/startup/session.php
(это первичный контроллер, который запускается при любом запросе в catalog
, до основного контроллера), здесь при наличии токена полученного в предыдущем запросе происходит старт сессии с именем api
:
$this->session->start('api', $query->row['session_id']);
А теперь пройдем в файл с классом сессии system/library/session.php
и смотрим метод start
:
public function start($key = 'default', $value = '') {
if ($value) {
$this->session_id = $value;
} elseif (isset($_COOKIE[$key])) {
$this->session_id = $_COOKIE[$key];
} else {
$this->session_id = $this->createId();
}
if (!isset($_SESSION[$this->session_id])) {
$_SESSION[$this->session_id] = array();
}
$this->data = &$_SESSION[$this->session_id];
//...
}
На основании предыдущих файлов можно сказать: session_id
может быть взят из куки api
, а данные сессии можно получить по session_id
.
Вспоминаем что API запросы OpenCart проверяют валидность доступа в catalog
контекст по токену следующим образом:
if (!isset($this->session->data['api_id'])) {
$json['error']['warning'] = $this->language->get('error_permission');
} else {
...
}
И приходим к понимаю того, что с сессией проблемы, хотя судя по коду должно быть все ровно.
Для того чтобы понять в чем проблема, можно попробовать записывать данные $_SESSION
в файл в запросе получения токена (после его получения), и при первом API запросе в файле catalog/controller/startup/session.php
прямо перед или после старта api
сессии.
В итоге я увидел что:
Сессия с
session_id
создается на этапе авторизации и в нее записывается один единственный ключapi_key
, но уже при следующем запросе к API, массив данных сессии с этимsession_id
пуст, но заполняется при отработке всех контроллеров указанных в массивеaction_pre_action
(в файлеsystem/config/catalog.php
) и данные сессии сохраняются.Однако при этом в нем отсутствует ключ
api_id
, без которого дальнейшая работа с API невозможна и поэтому мы видим сообщение: У вас нет разрешения на доступ к API!
Решение проблем
Почему возникает данная проблема мне не удалось выяснить. Развернув точную копию сайта клиента у себя на локальном сервере, воспроизвести проблему не получилось.
После обращения клиента в ТП хостинга, проблема исчезла на некоторое время (в это же время я пытался решить ее самостоятельно, но безуспешно, ибо не воспроизводилось), а потом благополучно, примерно через сутки, проблема вернулась.
Повторный дебаг $_SESSION
не дал результатов, все также: при запросе авторизации создавалась новая сессия с ключом api_id
, а при следующем обращении к API эта новая сессия была пуста.
Понимая что данные сессии не могут сохранится в $_SESSION
при запросе авторизации снова смотрим catalog/controller/startup/session.php
и видим запрос к БД:
$query = $this->db->query("SELECT DISTINCT * FROM `" . DB_PREFIX . "api` `a` LEFT JOIN `" . DB_PREFIX . "api_session` `as` ON (a.api_id = as.api_id) LEFT JOIN " . DB_PREFIX . "api_ip `ai` ON (as.api_id = ai.api_id) WHERE a.status = '1' AND as.token = '" . $this->db->escape($this->request->get['token']) . "' AND ai.ip = '" . $this->db->escape($this->request->server['REMOTE_ADDR']) . "'");
Если этот запрос возвращает не пустой массив, значит можно считать что авторизация прошла успешно, а среди выбранных из БД данных есть api_id
.
В catalog/controller/startup/session.php
сразу после старта сессии пишем:
if ($query->num_rows) {
$this->session->start('api', $query->row['session_id']);
//это может решить проблему с доступом по апи
$this->session->data["api_id"] = $query->row["api_id"];
//...
}
И проблема У вас нет разрешения на доступ к API! решена!
Для решения проблемы У вас нет разрешения на доступ к API! в данном случае достаточно после старта сессии в
catalog/controller/startup/session.php
вставить в массив сессии ключapi_id
:$this->session->data["api_id"] = $query->row["api_id"];
Для убедительности я провел тест: получил ошибку У вас нет разрешения на доступ к API!, а затем применил описанное выше решение и не перезагружая страницы (где была ошибка) провел несколько ajax запросов к API OpenCart, которые прошли успешно.
Итог
Клиент доволен, оба сайта работают, проблема решена. Однако, источник проблемы не выявлен, есть несколько предположений, возможно как-нибудь проверю.
taluyev
Пример реализации REST API для OpenCart.
https://github.com/QuasarByte/qbestore-opencart-rest-api-extension