Содержание


Создание и развертывание масштабируемого приложения для управления контактами в облаке. Часть 2

Добавление нескольких пользователей и развертывание в Bluemix

Comments

СУБД MongoDB стала популярным выбором для облачных приложений, управляемых базой данных, благодаря своей гибкой, документально-ориентированной схеме и простой масштабируемости. В этом руководстве из двух частей показано, как использовать IBM® Bluemix™ и его службу MongoLab для создания и развертывания веб-приложения «Адресная книга» с помощью PHP, MongoDB и jQuery Mobile.

Чтобы продемонстрировать, как это легко, Часть 1 познакомила вас с процессом создания размещенного в облаке IBM Bluemix PHP/jQuery-приложения «Адресная книга», которое позволяет хранить контактные адреса и управлять ими с помощью базы данных MongoDB. В Части 2 я покажу, как добавить поддержку нескольких пользователей, развернуть приложение в Bluemix и сразу же начать использовать его на планшете или смартфоне.

Что нужно для развертывания приложения

Примечание. В этом руководстве предполагается, что вы выполнили шаги, описанные в Части 1.

Пример приложения позволяет вводить контактные данные, включая имя, адрес электронной почты и номера телефонов. Приложение работает в облаке Bluemix и сразу же может использоваться на сенсорных устройствах, таких как планшеты и смартфоны. Оно также поддерживает несколько пользователей, позволяя им входить в приложение с помощью своих реквизитов Google+. Проверка подлинности выполняется с помощью протокола OAuth 2.0 и API Google+.

Со стороны клиента я использую jQuery Mobile для создания пользовательского интерфейса приложения, оптимизированного для мобильных устройств. На сервере я использую PHP-микросреду Slim, которая позволяет управлять логикой приложения и сохранять/извлекать данные из MongoDB, а также библиотеку с открытым исходным кодом HybridAuth для аутентификации пользователей.

Так что здесь применяется множество технологий, но вам понадобится вот что:

Примечание. Любое приложение, использующее API-интерфейс Google+, должно соблюдать условия обслуживания и политику конфиденциальности Google. Перед началом проекта потратьте несколько минут на чтение этих правил и следите за тем, чтобы в вашем приложении они соблюдались.

Запустить приложениеПолучить код

Шаг 1. Настройка OAuth

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

На техническом уровне это подразумевает две вещи:

  • прикрепление к каждой записи контактов идентификатора пользователя, так чтобы каждый пользователь видел только контакты, связанные с его учетной записью;
  • добавление дополнительного уровня проверки подлинности, так чтобы каждый пользователь идентифицировался, прежде чем получить доступ к приложению.

Гибкая, документально-ориентированная схема MongoDB делает выполнение первого пункта тривиальным (я покажу это в следующем разделе). Второй элемент несколько сложнее, потому что сначала нужно решить, развертывать ли свою собственную систему проверки подлинности или позволить пользователям входить с помощью их учетных записей в других сервисах (Facebook, Twitter, Google+ и т.п.).

В этом руководстве я планирую выбрать последнее... а инструментом, который я для этого использую, будет HybridAuth – PHP-библиотека с открытым исходным кодом, которая позволяет аутентифицировать пользователей в широком спектре социальных сетей и сервисов. HybridAuth абстрагирует от тонкостей служб аутентификации и авторизации, что облегчает добавление входа в PHP-приложение через социальную сеть без сложностей, связанных с операциями OAuth и маркерами доступа.

  1. Чтобы приступить к использованию библиотеки HybridAuth, добавьте ее в свой проект, обновив файл $APP_ROOT/composer.json:
    { 
        "require": { 
            "slim/slim": "2.*", 
            "hybridauth/hybridauth": "2.*" 
        } 
    }
  2. Установите библиотеку HybridAuth с помощью команды:
    shell> cd /usr/local/apache/htdocs/contacts 
    shell> php composer.phar update
  3. Решите, какую социальную сеть использовать для входа. Для простоты я использую Google+, хотя так же легко использовать Twitter, Facebook, MySpace, LinkedIn или Foursquare (или же все эти сети вместе). Сначала нужно зарегистрировать веб-приложение в Google и предоставить ему доступ к API Google+ для проверки подлинности.
  4. Войдите в Google, используя свои учетные данные Google, и откройте Google Developers Console.
  5. Создайте новый проект, дайте ему имя, а затем включите доступ к API Google+ в разделе APIs проекта:
  6. Пока вы там, получите ID клиента OAuth 2.0 и секретный ключ приложения в разделе Credentials проекта:

    При получении ID клиента OAuth и секретного ключа задайте перенаправление URL-адреса приложения. Это URL-адрес, к которому Google будет перенаправлять браузер клиента после завершения процесса аутентификации OAuth. В данном примере URL-адрес должен быть http://localhost/contacts/callback?hauth.done=Google. Этот специальный маршрут /callback будет определен в приложении позднее.

Примечание. Не забудьте прочесть условия Google APIs Terms of Service, Google+ Platform Developer Policies, Google+Platform Terms of Service и Google Privacy Policies, чтобы убедиться, что ваше приложение полностью им соответствует. Например, нужно предоставить пользователям возможность в любой момент удалить все свои данные из вашей системы. Пример необходимого для этого кода приведен в репозитории кода приложения.

Шаг 2. Поддержка нескольких пользователей

Выполнив всю подготовительную работу, займемся написанием кода. Настройте HybridAuth в приложении, добавив в файл $APP_ROOT/index.php следующие строки:

<?php 
// Использование автозагрузчика Composer 
require 'vendor/autoload.php'; 
 
// Инициализация Slim – сокращено! 
 
// Настройка реквизитов доступа 
// ... к HybridAuth 
$config["hybridauth"]  = array( 
  "base_url" => $app->request()->getUrl() . $app->request()->getRootUri() . "/callback",   
  "providers" => array ( 
  "Google" => array ( 
    "enabled" => true, 
    "keys" => array ("id" => "YOUR_CLIENT_ID_HERE", "secret" => "YOUR_CLIENT_SECRET_HERE_huJ" ), 
    "scope" => "https://www.googleapis.com/auth/userinfo.email" 
))); 
 
// Инициализация клиента HybridAuth 
$auth = new Hybrid_Auth($config["hybridauth"]); 
 
// Обработчики и перехватчики Slim – сокращено! 
 
$app->run();

Приведенный выше код инициализирует новый клиент HybridAuth, доступный через объект $auth. Очевидно, что фиктивные ID клиента и секретный ключ в приведенной выше конфигурации нужно заменить теми, которые были созданы для вашего приложения на шаге 1.

Далее, добавим некоторые маршруты для входа и выхода вместе со специальным маршрутом /callback, необходимым для OAuth-аутентификации:

<?php 
// Настройка и инициализация – сокращено! 
 
session_start(); 
 
// Обработчик входа 
$app->get('/login', function () use ($app, $auth) { 
  $google = $auth->authenticate("Google"); 
  $currentUser = $google->getUserProfile(); 
  $_SESSION['uid'] = $currentUser->email; 
  $app->redirect($app->urlFor('index')); 
})->name('login'); 
 
// Обработчик выхода 
$app->get('/logout', function () use ($app, $auth) { 
  $auth->logoutAllProviders(); 
  session_destroy(); 
  $app->render('logout.tpl.php'); 
}); 
 
// Обработчик обратного вызова OAuth 
$app->get('/callback', function () { 
  Hybrid_Endpoint::process(); 
}); 
 
// Другие обработчики и перехватчики – сокращено! 
 
$app->run();

Обработчик /login использует метод authenticate() клиента HybridAuth для проверки подлинности пользователя с помощью API Google+. Метод authenticate() абстрагирует от всех сложностей процессов аутентификации и авторизации OAuth. После проверки подлинности метод getUserProfile() результирующего объекта возвращает профиль авторизованного пользователя в структурированном формате. Этот профиль включает в себя имя пользователя, URL-адрес профиля, URL-адрес фотографии и другую личную информацию, но для целей данного приложения все, что вам действительно нужно, – это адрес электронной почты, связанный с пользователем, который будет служить его уникальным идентификатором. Этот идентификатор сохраняется в переменной сеанса $_SESSION['uid'], и клиент направляется на страницу index приложения.

Для логики OAuth-аутентификации требуется обработчик /callback, так как это URL-адрес, к которому Google будет перенаправлять браузер клиента после завершения процесса аутентификации. Метод Hybrid_Endpoint::process() абстрагирует обработку обратного вызова.

Обработчик /logout использует метод logoutAllProviders() объекта HybridAuth для выхода из всех подключенных служб. Он также прерывает текущий сеанс приложения и отображает следующий шаблон страницы, который нужно сохранить как файл $APP_ROOT/templates/logout.tpl.php:

<!DOCTYPE html>  
<html>  
<head>  
  <meta name="viewport" content="width=device-width, initial-scale=1">  
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.css" /> 
  <script src="//code.jquery.com/jquery-1.9.1.min.js"></script> 
  <script src="//code.jquery.com/mobile/1.4.2/jquery.mobile-1.4.2.min.js"></script> 
</head>  
<body> 
  <div data-role="page"> 
    <div data-role="header"> 
      <h1>Contacts</h1> 
    </div> 
    <div data-role="content"> 
      You are now signed out of the application. However, since you originally signed in using your Google Account, you must also sign out of your Google Account to completely destroy your session. 
      <br/> 
      <a href="<?php echo $baseUri; ?>/index" data-ajax="false" data-inline="true" data-role="button" data-icon="back" data-mini="true" data-theme="a">Back</a> 
      <a href="https://accounts.google.com/Logout" data-ajax="false" data-inline="true" data-role="button" data-icon="action" data-mini="true" data-theme="a">Sign out of Google</a> 
    </div>     
  </div> 
</body> 
</html>

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

Поскольку переменная $_SESSION['uid'] существует только в том случае, если пользователь успешно прошел аутентификацию, ее можно использовать для защиты доступа к другим маршрутам приложения, как показано ниже:

<?php 
// Настройка и инициализация – сокращено! 
 
session_start(); 
 
// Промежуточное ПО для ограничения доступа пользователями, прошедшими проверку подлинности 
function authenticate () { 
  $app = \Slim\Slim::getInstance(); 
  if (!isset($_SESSION['uid'])) { 
    $app->redirect($app->urlFor('login')); 
  } 
} 
 
// Другие обработчики и перехватчики – сокращено! 
 
$app->run();

Приведенная выше функция authenticate() проверяет наличие идентификатора пользователя в сеансе. Если его нет, пользователь перенаправляется на URL /login для повторного входа. Эта функция используется как промежуточное ПО Slim и запускается перед обработкой запроса. Добавление этого промежуточного ПО к обработчикам конкретных маршрутов делает возможным защиту доступа к функциям приложения, доступным только аутентифицированным пользователям. Если вы посмотрите на исходный код приложения, то увидите это промежуточное ПО в составе обработчиков /index, /save, /delete и /logout.

С учетом вышеуказанных изменений пользователь может получить доступ к функциональности приложения только после входа в свою учетную запись Google. Но остался еще один шаг: отредактировать код приложения для присоединения адресов электронной почты пользователей (которые, как вы помните, всегда хранятся в переменной $_SESSION['uid']) к каждой записи контактов и отфильтровать все запросы MongoDB с использованием этого уникального идентификатора.

Вот окончательный код со всеми перечисленными изменениями:

<?php 
// Использование автозагрузчика Composer 
require 'vendor/autoload.php'; 
 
// Настройка экземпляра приложения Slim 
// Инициализация приложения 
$app = new \Slim\Slim(array( 
  'debug' => true, 
  'templates.path' => './templates' 
)); 
 
// Настройка реквизитов доступа 
// ... к MongoDB 
$config["db"]["uri"] = 'mongodb://db2:db2@192.168.56.101:27017/db2'; 
$config["db"]["name"] = substr(parse_url($config["db"]["uri"], PHP_URL_PATH), 1); 
 
// ... к HybridAuth 
$config["hybridauth"]  = array( 
  "base_url" => $app->request()->getUrl() . $app->request()->getRootUri() . "/callback", 
  "providers" => array ( 
  "Google" => array ( 
    "enabled" => true, 
    "keys" => array ("id" => "YOUR_CLIENT_ID_HERE", "secret" => "YOUR_CLIENT_SECRET_HERE_huJ" ), 
    "scope" => "https://www.googleapis.com/auth/userinfo.email" 
))); 
 
// Начало сеанса 
session_start(); 
 
// Инициализация объекта клиента Mongo 
$mongo = new MongoClient($config["db"]["uri"], array("connectTimeoutMS" => 30000)); 
$db = $mongo->selectDb($config["db"]["name"]); 
 
// Инициализация клиента HybridAuth 
$auth = new Hybrid_Auth($config["hybridauth"]); 
 
// Обработчики страницы index  
$app->get('/', function () use ($app) { 
  $app->redirect($app->urlFor('index')); 
}); 
 
$app->get('/index', 'authenticate', function () use ($app, $db) { 
  $collection = $db->contacts; 
  $contacts = $collection->find(array('owner' => $_SESSION['uid'])); 
  $app->render('main.tpl.php', array('contacts' => $contacts)); 
})->name('index'); 
 
// Обработчики добавления и редактирования 
$app->get('/save(/:id)', 'authenticate', function ($id = null) use ($app, $db) { 
  $collection = $db->contacts; 
  $contact = $collection->findOne(array('_id' => new MongoId($id), 'owner' => $_SESSION['uid'])); 
  $app->render('form.tpl.php', array('contact' => $contact)); 
}); 
 
$app->post('/save', 'authenticate', function () use ($app, $db) { 
  $collection = $db->contacts;   
  $name = trim(strip_tags($app->request->params('name'))); 
  $id = trim(strip_tags($app->request->params('id'))); 
  $email = trim(strip_tags($app->request->params('email'))); 
  $phone = trim(strip_tags($app->request->params('phone'))); 
  $contact = new stdClass; 
  if (!empty($name)) { 
    $contact->name = $name; 
    $contact->owner = $_SESSION['uid']; 
    $contact->phone = $phone; 
    $contact->email = $email; 
    if (!empty($id)) { 
      $contact->_id = new MongoId($id); 
    } 
    $collection->save($contact); 
  } 
  $app->redirect($app->urlFor('index')); 
}); 
 
// Обработчик удаления 
$app->get('/delete/:id', 'authenticate', function ($id) use ($app, $db) { 
  $collection = $db->contacts; 
  $collection->remove(array('_id' => new MongoId($id), 'owner' => $_SESSION['uid'])); 
  $app->redirect($app->urlFor('index')); 
}); 
 
// Обработчик входа 
$app->get('/login', function () use ($app, $auth) { 
  $google = $auth->authenticate("Google"); 
  $currentUser = $google->getUserProfile(); 
  $_SESSION['uid'] = $currentUser->email; 
  $app->redirect($app->urlFor('index')); 
})->name('login'); 
 
// Обработчик выхода 
$app->get('/logout', 'authenticate', function () use ($app, $auth) { 
  $auth->logoutAllProviders(); 
  session_destroy(); 
  $app->render('logout.tpl.php'); 
}); 
// OAuth callback handler 
$app->get('/callback', function () { 
  Hybrid_Endpoint::process(); 
}); 
 
 
// перехватчик для добавления пути URI запроса в качестве переменной шаблона 
$app->hook('slim.before.dispatch', function() use ($app) { 
  $app->view()->appendData(array( 
    'baseUri' => $app->request()->getRootUri() 
  )); 
});  
 
$app->run(); 
 
// Промежуточное ПО для ограничения доступа пользователями, прошедшими проверку подлинности 
function authenticate () { 
  $app = \Slim\Slim::getInstance(); 
  if (!isset($_SESSION['uid'])) { 
    $app->redirect($app->urlFor('login')); 
  } 
}

Как видно из выделенных разделов, обработчик /index теперь отображает только контакты, принадлежащие зарегистрированному пользователю, а обработчик /save автоматически присоединяет уникальный идентификатор пользователя к каждому вновь созданному контакту. Теперь приложение поддерживает несколько пользователей, каждый со своим собственным уникальным подмножеством записей контактов.

Чтобы проверить это, попробуйте просмотреть приложение, как раньше, по адресу http://localhost/contacts. На этот раз, вместо того чтобы сразу увидеть список контактов, вы получите предложение войти в свою учетную запись Google. Сделав это, вы должны увидеть экран авторизации OAuth:

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

Шаг 3. Развертывание в Bluemix

Теперь, когда код приложения готов, на последнем шаге его нужно развернуть. Конечно, если сделать это локально, то все будет в порядке - приложение можно будет использовать как обычно. Но для развертывания в Bluemix вам понадобится учетная запись Bluemix и клиент командной строки Cloud Foundry. Для завершения процесса развертывания выполните следующие действия.

  1. При развертывании в Bluemix нужно привязать к своему приложению экземпляр службы MongoDB. Эта служба предоставляется через Bluemix (я вскоре покажу, как), а учетные данные для новой базы данных MongoDB предоставляются через специальную переменную среды VCAP_SERVICES. Таким образом, необходимо отредактировать код приложения для считывания этой переменной и извлечения из него учетных данных MongoDB.

    Сделайте следующее дополнение к своему файлу $APP_ROOT/index.php:

    <?php 
    // Использование автозагрузчика Composer 
    require 'vendor/autoload.php'; 
     
    // Настройка экземпляра приложения Slim 
    // Инициализация приложения 
    $app = new \Slim\Slim(array( 
      'debug' => true, 
      'templates.path' => './templates' 
    )); 
     
    // Настройка учетных данных – сокращено! 
     
    // Если переменная среды BlueMix VCAP_SERVICES существует, 
    // перезаписать в нее учетные данные из BlueMix 
    if ($services = getenv("VCAP_SERVICES")) { 
      $services_json = json_decode($services, true); 
      $config["db"]["uri"] = $services_json["mongolab"][0]["credentials"]["uri"]; 
    }  
    $config["db"]["name"] = substr(parse_url($config["db"]["uri"], PHP_URL_PATH), 1); 
     
    // Начало сеанса 
    session_start(); 
     
    // Инициализация объекта клиента Mongo 
    $mongo = new MongoClient($config["db"]["uri"], array("connectTimeoutMS" => 30000)); 
    $db = $mongo->selectDb($config["db"]["name"]); 
     
    // Инициализация клиента HybridAuth 
    $auth = new Hybrid_Auth($config["hybridauth"]);  
     
    // Обработчики маршрутов – сокращено!
  2. Создайте манифест приложения. Файл манифеста приложения указывает Bluemix, как развертывать приложение. В частности, он указывает среду выполнения PHP (build pack), которую следует использовать. Создайте этот файл как $APP_ROOT/manifest.yml, заполнив его следующей информацией.
    --- 
    applications: 
    - name: contacts-[random-number] 
    memory: 256M 
    instances: 1 
    host: contacts-[random-number] 
    buildpack: https://github.com/dmikusa-pivotal/cf-php-build-pack.git

    Не забудьте отредактировать имена хоста и приложения, чтобы сделать их уникальными, – измените их или добавьте случайное число. Я использую пакет сборки CloudFoundry PHP, хотя есть и другие варианты.

  3. Настройте пакет сборки PHP. По умолчанию в пакет сборки PHP Cloud Foundry входят некоторые наиболее распространенные PHP-расширения. Расширение PHP MongoDB по умолчанию не включено, но его довольно легко добавить.

    Создайте каталог $APP_ROOT/.bp-config, затем файл $APP_ROOT/.bp-config/options.json со следующим содержимым для добавления поддержки расширения PHP MongoDB:

    { 
        "WEB_SERVER": "httpd", 
        "PHP_EXTENSIONS": ["bz2", "zlib", "curl", "mcrypt", "mongo"] 
    }

    По умолчанию веб-сервер Apache, включенный в пакет сборки, уже содержит включенную поддержку mod_rewrite и .htaccess, так что вам не нужно ничего больше делать, чтобы включить функции перезаписи URL Slim.

  4. Войдите в Bluemix и разверните приложение. Для входа в Bluemix используйте интерфейс командной строки cf с именем пользователя и паролем IBM:
    shell> cf api https://api.ng.bluemix.net 
    shell> cf login
  5. Перейдите в каталог $APP_ROOT и опубликуйте приложение в Bluemix:
    shell> cf push

    Вот пример того, что вы увидите в ходе этого процесса.

    Пример входа в BlueMix и развертывание приложения
    Пример входа в BlueMix и развертывание приложения
  6. Привяжите к своему приложению службу MongoLab.

    Теперь приложение развернуто, но его еще нужно подключить к экземпляру базы данных MongoDB, чтобы где-то хранить свои данные.

    1. Откройте панель администрирования Bluemix и войдите со своим именем пользователя и паролем IBM. Вы должны увидеть свое приложение в строке меню Apps: Панель администрирования Bluemix с приложением в строке меню Apps
      Панель администрирования Bluemix с приложением в строке меню Apps
    2. Выберите свое приложение и нажмите кнопку Add a service or API. Выберите службу MongoLab (в категории Data Management), чтобы добавить ее в свое приложение: Окно службы MongoLab
      Окно службы MongoLab
    3. Вам будет предложено переустановить приложение. Сделайте это, и вы увидите на панели управления Bluemix экземпляр службы MongoDB, привязанный к вашему приложению. Экземпляр службы MongoLab, привязанный к приложению
      Экземпляр службы MongoLab, привязанный к приложению
    4. Если щелкнуть на значке службы, откроется панель управления MongoLab, где можно управлять своим экземпляром службы MongoDB. Обратите внимание, что создавать базу данных MongoDB не нужно, так как экземпляр службы включает в себя готовую к применению базу данных по умолчанию:
    5. Проверив переменные среды своего приложения на панели управления Bluemix, вы также увидите URI своей базы данных MongoDB в переменной среды VCAP_SERVICES. Эта информация извлекается автоматически и используется приложением, как говорилось выше: Экран переменных среды
      Экран переменных среды
  7. Отредактируйте URI переадресации в Google Developers Console.

    Последним шагом откройте Google Developers Console и отредактируйте список разрешенных URI переадресации, включив в него имя хоста своего приложения в Bluemix. Без этого изменения перенаправление OAuth завершится ошибкой, и пользователи не смогут войти в приложение:

    Окно Edit Client Settings
    Окно Edit Client Settings
  8. Приступайте к работе с приложением. Когда приложение развернуто, можно приступать к его использованию по адресу, указанному в манифесте приложения — например, http://contacts-[случайное число].mybluemix.net. Или можно воспользоваться ссылками в начале этой статьи, чтобы увидеть демонстрацию приложения в действии. Если вы видите пустую страницу или другие ошибки, используйте ссылку в начале этого раздела для отладки PHP-кода и выяснения причины неполадок.

Заключение

Как показано в этом руководстве, PaaS-инфраструктура Bluemix и служба MongoLab в сочетании с пакетом сборки PHP CloudFoundry делают создание PHP-приложений с базой данных MongoDB в облаке простым делом. Комбинируя эти ключевые компоненты инфраструктуры с популярными инструментами и механизмами, такими как Slim, HybridAuth и jQuery Mobile, вы получаете непревзойденный инструментарий для создания прототипа, развертывания и масштабирования приложений в облаке.

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


Ресурсы для скачивания


Похожие темы


Комментарии

Войдите или зарегистрируйтесь для того чтобы оставлять комментарии или подписаться на них.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Облачные вычисления, Мобильные приложения
ArticleID=1030155
ArticleTitle=Создание и развертывание масштабируемого приложения для управления контактами в облаке. Часть 2
publish-date=04182016