Статьи

Создание PHP OAuth-сервера

Если вы когда-либо интегрировались с другим API, который требует безопасности (например, Twitter), вы, вероятно, использовали сервис OAuth. В этой статье я расскажу, что нужно для создания своего собственного трехстороннего сервера OAuth, позволяющего, например, создать собственный защищенный API, который вы можете опубликовать публично.

Имея дело с OAuth, вы обычно увидите, что он реализован в виде сервера OAuth с двумя или тремя ножками. Основное различие между ними заключается в том, что двухсторонняя аутентификация не затрагивает другого пользователя. Например, если вы хотите получить доступ к информации Twitter определенного пользователя, вы должны использовать трехсторонний сервер, поскольку для пользователя в вашем приложении должен быть создан токен доступа, а не просто Twitter, предоставляющий вам токен. Мы сосредоточимся на трехногом разнообразии, поскольку оно более практично для использования в реальных условиях.

Мы будем использовать oauth-php, чтобы выполнить большую часть тяжелой работы для нас. Библиотека размещена в Google Code и не указана в Packagist , но ее все равно можно установить с помощью Composer . За подробностями обращайтесь к файлу composer.json в коде, который сопровождает эту статью, доступную на GitHub.

Понимание потока

При использовании трехстороннего сервера OAuth типичный процесс, который разработчик предпримет для реализации и использования сервиса, выглядит следующим образом:

Приведенное выше изображение, любезно предоставленное OAuth.net , довольно сложное, но в простых сроках оно показывает следующее:

  • Потребитель запрашивает токен с сервера
  • Затем потребитель направляет пользователя на страницу входа, передавая ему токен
  • Пользователь входит в систему и перенаправляется обратно к потребителю с токеном доступа.
  • Потребитель берет токен доступа и запрашивает токен OAuth для использования с будущими безопасными запросами
  • Извлечен токен OAuth, и теперь разработчик может делать безопасные запросы, передавая токен для проверки.

Настройка базы данных

Когда библиотека oauth-php находится в доступном месте, необходимо создать и инициализировать новую базу данных. Я буду использовать скрипт схемы, находящийся в library/store/mysql/mysql.sql .

Если вы просмотрите таблицы, то увидите, что таблица oauth_server_registry содержит поле с именем osr_usa_id_ref . Это заполняется во время процесса регистрации сервером OAuth. Предполагается, что у вас уже есть таблица пользователей, с которой она будет связана. Если вы делаете, это прекрасно! Но если нет, то вот базовый SQL для создания стандартной пользовательской таблицы:

 CREATE TABLE users ( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, name VARCHAR(255) NOT NULL DEFAULT '', password VARCHAR(255) NOT NULL DEFAULT '', email VARCHAR(255) NOT NULL DEFAULT '', created DATE NOT NULL DEFAULT '0000-00-00', PRIMARY KEY (id) ); 

Создание OAuth-сервера

Давайте начнем писать сервер OAuth. Следующее является общим для остальной части нашего кода, поэтому я поместил его в отдельный файл include/common.php :

 <?php require_once '../vendor/autoload.php'; session_start(); // Add a header indicating this is an OAuth server header('X-XRDS-Location: http://' . $_SERVER['SERVER_NAME'] . '/services.xrds.php'); // Connect to database $db = new PDO('mysql:host=localhost;dbname=oauth', 'dbuser', 'dbpassword'); // Create a new instance of OAuthStore and OAuthServer $store = OAuthStore::instance('PDO', array('conn' => $db)); $server = new OAuthServer(); 

Файл добавляет дополнительный HTTP-заголовок к каждому запросу, чтобы сообщить клиентам, что это сервер OAuth. Обратите внимание, что он ссылается на services.xrds.php ; этот файл снабжен примером, который поставляется с библиотекой oauth-php. Вы должны скопировать его из example/server/www/services.xrds.php в корневой публичный каталог веб-сервера.

Следующие несколько строк кода устанавливают соединение с базой данных (информация о соединении должна обновляться в соответствии с вашими настройками) и создает новые экземпляры объектов OAuthStore и OAuthServer предоставляемых библиотекой.

Настройка для сервера OAuth завершена, и сервер готов к полной реализации. В остальных примерах файл includes/common.php должен включаться каждый раз для создания экземпляра сервера.

Разрешение регистрации

Прежде чем разработчики смогут использовать ваш OAuth-сервер, они должны зарегистрироваться на нем. Для этого нам нужно создать базовую регистрационную форму. Следующие поля являются обязательными, поскольку они передаются в библиотеку: requester_name и requester_email . Остальные поля являются необязательными: application_uri и callback_uri .

 <form method="post" action="register.php"> <fieldset> <legend>Register</legend> <div> <label for="requester_name">Name</label> <input type="text" id="requester_name" name="requester_name"> </div> <div> <label for="requester_email">Email</label> <input type="text" id="requester_email" name="requester_email"> </div> <div> <label for="application_uri">URI</label> <input type="text" id="application_uri" name="application_uri"> </div> <div> <label for="callback_uri">Callback URI</label> <input type="text" id="callback_uri" name="callback_uri"> </div> </fieldset> <input type="submit" value="Register"> </form> 

Как я упоминал ранее, в библиотеке предполагается, что у вас уже есть пользователи, которые хотят использовать ваш сервер. В следующем коде я создаю нового пользователя в таблице users , затем извлекаю ID и затем updateConsumer() методу updateConsumer() создавая (или обновляя) ключ и секретный ключ для этого пользователя. Когда вы интегрируете это в свое приложение, эта часть должна быть изменена и помещена в существующий процесс входа в систему, где вы уже знаете, кто пользователь, который регистрируется для доступа.

 <?php $stmt = $db->prepare('INSERT INTO users (name, email, created) ' . 'VALUES (:name, :email, NOW())'); $stmt->execute(array( 'name' => $_POST['requester_name'], 'email' => $_POST['requester_email'] )); $id = $db->lastInsertId(); $key = $store->updateConsumer($_POST, $id, true); $c = $store->getConsumer($key, $id); ?> <p><strong>Save these values!</strong></p> <p>Consumer key: <strong><?=$c['consumer_key']; ?></strong></p> <p>Consumer secret: <strong><?=$c['consumer_secret']; ?></strong></p> 

По завершении регистрации выводятся новый потребительский ключ пользователя и секретный ключ потребителя. Эти значения должны быть сохранены пользователем для дальнейшего использования.

Теперь, когда пользователь зарегистрирован, он может начать делать запросы на токен доступа!

Генерация токена запроса

Как только пользователь зарегистрировался, он должен выполнить запрос OAuth в ваш файл request_token.php . Этот файл (еще раз из-за библиотеки) очень прост:

 <?php require_once 'include/oauth.php'; $server->requestToken(); 

Метод requestToken() заботится о проверке того, что пользователь предоставил действительный ключ и подпись потребителя. Если запрос действителен, возвращается новый токен запроса.

Обмен токена запроса на токен доступа

Пользователь должен быть перенаправлен на вашу страницу входа в систему после создания токена запроса. Эта страница должна ожидать следующие параметры URL: oauth_token и oauth_callback .

Страница входа должна получить пользователя из таблицы пользователей. oauth_token идентификатор пользователя (вместе с oauth_token ) oauth_token в метод authorizeVerify() предоставляемый библиотекой. Предполагая, что пользователь авторизовал приложение, идентификатор вошедшего в систему пользователя затем связывается с ключом потребителя, что позволяет ему обеспечить безопасный доступ к данным этого пользователя.
Необходимая логика базового login.php может выглядеть следующим образом:

 <?php // check if the login information is valid and get the user's ID $sql = 'SELECT id FROM users WHERE email = :email'; $stmt = $db->prepare($sql); $result = $stmt->exec(array( 'email' => $_POST['requester_email'] )); $row = $result->fetch(PDO::FETCH_ASSOC); if (!$row) { // incorrect login } $id = $row['id']; $result->closeCursor(); // Check if there is a valid request token in the current request. // This returns an array with the consumer key, consumer secret, // token, token secret, and token type. $rs = $server->authorizeVerify(); // See if the user clicked the 'allow' submit button (or whatever // you choose) $authorized = array_key_exists('allow', $_POST); // Set the request token to be authorized or not authorized // When there was a oauth_callback then this will redirect to // the consumer $server->authorizeFinish($authorized, $id); 

После того, как пользователь войдет в систему, он будет перенаправлен обратно на веб-сайт потребителя-разработчика (через параметр oauth_callback ) с действительным токеном. Этот токен и ключ проверки могут затем использоваться в обмене на действительный токен доступа.

Основной файл access_token.php выглядит следующим образом:

 <?php require_once 'include/oauth.php'; $server->accessToken(); 

Этот файл так же прост, как и ранее созданный request_token.php . Вся работа выполняется внутри метода accessToken accessToken() предоставляемого библиотекой oauth-php. После успешного запроса oauth_token действительные oauth_token и oauth_token_secret , которые должны храниться и использоваться с будущими запросами к вашему API.

Подтверждение запроса

На этом этапе сервер OAuth запущен и работает. Но нам все еще нужно убедиться, что запрос содержит действительную подпись OAuth. Я создал основной тестовый файл, который делает именно это:

 <?php require_once 'includes/oauth.php'; if (OAuthRequestVerifier::requestIsSigned()) { try { $req = new OAuthRequestVerifier(); $id = $req->verify(); // If we have a user ID, then login as that user (for // this request) if ($id) { echo 'Hello ' . $id; } } catch (OAuthException $e) { // The request was signed, but failed verification header('HTTP/1.1 401 Unauthorized'); header('WWW-Authenticate: OAuth realm=""'); header('Content-Type: text/plain; charset=utf8'); echo $e->getMessage(); exit(); } } 

В этом примере, если запрос подтвержден, я просто отображаю идентификатор пользователя, который вошел в систему. Я бы предложил создать метод многократного использования, содержащий этот код, для любых вызовов API, требующих безопасности.

Тестирование OAuth-сервера

Наконец, пришло время протестировать сервер OAuth. Ниже приведен простой тестовый файл, который выполняет вышеуказанные шаги, чтобы потребовать от пользователя входа в систему и выполняет безопасный запрос:

 <?php define('OAUTH_HOST', 'http://' . $_SERVER['SERVER_NAME']); $id = 1; // Init the OAuthStore $options = array( 'consumer_key' => '<MYCONSUMERKEY>', 'consumer_secret' => '<MYCONSUMERSECRET>', 'server_uri' => OAUTH_HOST, 'request_token_uri' => OAUTH_HOST . '/request_token.php', 'authorize_uri' => OAUTH_HOST . '/login.php', 'access_token_uri' => OAUTH_HOST . '/access_token.php' ); OAuthStore::instance('Session', $options); if (empty($_GET['oauth_token'])) { // get a request token $tokenResultParams = OauthRequester::requestRequestToken($options['consumer_key'], $id); header('Location: ' . $options['authorize_uri'] . '?oauth_token=' . $tokenResultParams['token'] . '&oauth_callback=' . urlencode('http://' . $_SERVER['SERVER_NAME'] . $_SERVER['PHP_SELF'])); } else { // get an access token $oauthToken = $_GET['oauth_token']; $tokenResultParams = $_GET; OAuthRequester::requestAccessToken($options['consumer_key'], $tokenResultParams['oauth_token'], $id, 'POST', $_GET); $request = new OAuthRequester(OAUTH_HOST . '/test_request.php', 'GET', $tokenResultParams); $result = $request->doRequest(0); if ($result['code'] == 200) { var_dump($result['body']); } else { echo 'Error'; } } 

OAuth требует, чтобы к каждому запросу добавлялись метки времени и подписи, и эта библиотека снова сделает это за нас.

Первая часть приведенного выше кода — это информация о конфигурации, которая должна быть обновлена ​​в соответствии с вашими потребностями. Идентификатор пользователя, ключ потребителя и секретный ключ потребителя генерируются во время процесса регистрации на сервере.

Как описано во время ознакомления с OAuth-сервером с тремя ножками, в вышеуказанном тестовом файле выполняется следующий процесс:

  • Запросить токен запроса (через файл request_token.php ) с ключом потребителя
  • После получения токена перенаправьте пользователя на страницу входа, передав URL-адрес токена и обратного вызова через параметры URL.
  • Как только пользователь вошел в систему, они перенаправлены обратно на тестовую страницу выше. Тестовая страница берет токен и запрашивает токен доступа (через файл access_token.php )
  • В случае успеха возвращается необходимая информация OAuth, и тестовый файл выполняет безопасный запрос к test_request.php .
  • Если все пойдет хорошо, будет отображено «Hello 1».

Резюме

На этом этапе вы должны знать, как создать базовый сервер OAuth. Используя файл test_request.php качестве примера, вы можете начать создавать дополнительные функции, защищенные с помощью Oauth! Если вы хотите поиграть с некоторым кодом, полный исходный код этой статьи доступен на GitHub.

Изображение через Fotolia

И если вам понравилось читать этот пост, вы полюбите Learnable ; место, чтобы узнать новые навыки и приемы у мастеров. Участники получают мгновенный доступ ко всем электронным книгам SitePoint и интерактивным онлайн-курсам, таким как Jump Start PHP .

Комментарии к этой статье закрыты. Есть вопрос по PHP? Почему бы не спросить об этом на наших форумах ?