Содержание


Sentry 2 и PHP

Часть 1. Аутентификация и управление доступом для PHP

Comments

Серия контента:

Этот контент является частью # из серии # статей: Sentry 2 и PHP

Следите за выходом новых статей этой серии.

Этот контент является частью серии:Sentry 2 и PHP

Следите за выходом новых статей этой серии.

При создании нового Web-приложения постоянно приходится писать типовые фрагменты кода, такие как форма входа, регистрационная форма, процесс восстановления пароля или менеджер учетной записи пользователя. Поскольку в настоящее время эти процессы сильно стандартизированы, 99% времени уходит на написание одного и того же кода с незначительными изменениями. Так почему бы не сэкономить время и не оформить его в виде набора классов?

Здесь на помощь приходит Sentry 2 – разработанная компанией Cartalyst и распространяемая на условиях лицензии BSD-3 "применимая в любой инфраструктуре система аутентификации и авторизации", написанная на PHP. Она предоставляет набор классов, которые упрощают добавление в PHP-приложение стандартных потоков аутентификации и управления доступом без ущерба для безопасности.

В этой серии статей, состоящей из двух частей (см. часть 2), описывается программный интерфейс Sentry 2 API, а также демонстрируется его интеграция и использование в Web-приложении, написанном на PHP. В статье приводятся примеры создания регистрационной формы с активацией по электронной почте, реализации процесса восстановления пароля, поиска учетных записей пользователей с помощью различных фильтров, а также создания, изменения и удаления учетных записей пользователей. Итак, приступим!

Установка

Предполагается, что вы знакомы с HTML и SQL и что у вас установлена среда разработки Apache/PHP/MySQL. Также предполагается, что вы знакомы с основами работы с классами и объектами в PHP, поскольку пакет Sentry 2 и пример кода, сопровождающие статью, используют эти понятия.

Sentry 2 можно использовать в составе PHP-инфраструктуры (FuelPHP, CodeIgniter и Laravel поддерживаются по умолчанию) или как самостоятельный пакет, который можно интегрировать в собственные приложения, созданные как с помощью, так и без помощи инфраструктуры. Для лучшего понимания примеров в статье будут использоваться процедурные PHP-сценарии и автозагрузчик Composer. Тем не менее в реальных приложениях лучше использовать PHP-инфраструктуру и загружать классы Sentry 2 с помощью загрузчика классов вашей инфраструктуры.

Для использования Sentry 2 в PHP-приложении необходимо сначала загрузить и установить пакет с его зависимостями. Проще всего это можно сделать с помощью Composer, который нужно загрузить и установить (см. раздел Ресурсы). После установки Composer создайте рабочий каталог (для удобства назовем его $PROJECT), а затем файл $PROJECT/composer.json с информацией из листинга 1.

Листинг 1. Создание файла $PROJECT/composer.json
{
    "name": "myapp/sentry",
    "minimum-stability": "dev",
    "require": {
        "cartalyst/sentry": "2.0.*",
        "illuminate/database": "4.0.*",
        "ircmaxell/password-compat": "1.0.*"
    }
}

Затем запустите Composer (см. листинг 2). Он загрузит пакет Sentry 2 с зависимостями и установит автозагрузчик.

Листинг 2. Запуск Composer
shell> php composer.phar self-update
shell> php composer.phar install

После окончания этого процесса каталог $PROJECT/vendor/ будет выглядеть примерно так, как показано на рисунке 1.

Рисунок 1. Структура каталогов после выполнения Composer
Структура каталогов после выполнения Composer

Теперь нужно создать таблицы базы данных для хранения информации о пользователях, группах и полномочиях. Sentry 2 поставляется с SQL-файлом, содержащим необходимые команды для создания этих таблиц. Это файл $PROJECT/vendor/cartalyst/sentry/schema/mysql.sql. Перед его использованием создайте пустую базу данных MySQL:

mysql> CREATE DATABASE appdata;

Затем импортируйте таблицы в MySQL, используя следующую команду:

shell> mysql -D appdata -u user -p < mysql.sql

При помощи команды SHOW TABLES убедитесь, что таблицы успешно созданы.

mysql> SHOW TABLES;

Вы должны увидеть выходные данные, показанные на рисунке 2.

Рисунок 2. Таблицы базы данных, используемые Sentry 2
Таблицы базы данных, используемые Sentry 2

Большинство примеров в этой статье использует таблицу users. Чтобы увидеть структуру этой таблицы, используйте команду DESC.

mysql> DESC users;

На рисунке 3 показана структура этой таблицы.

Рисунок 3. Структура таблицы users
Структура таблицы users
Структура таблицы users

Чтобы облегчить работу с примерами, нужно вставить одну запись о пользователе (см. листинг 3).

Листинг 3. Вставка одной записи о пользователе
INSERT INTO `users` (`id`, `email`, `password`, `permissions`, `activated`, 
 `activation_code`, `activated_at`, `last_login`, `persist_code`, 
 `reset_password_code`, `first_name`, `last_name`, `created_at`, `updated_at`) 
 VALUES
 (1, 'vikram@example.com', '$2y$10$GbX.pVNcGdlDfyDudmE.U.PF7c/
  ZajWhKLI9VZ23Ut.mXbQteDBZG', NULL, 1, NULL, NULL, NULL, NULL, NULL, 
  'Example', 'User', '2013-09-12 11:28:31', '2013-09-12 11:28:31');

Обратите внимание, что запрос SQL в листинге 3 создает запись о пользователе с зашифрованным паролем. В системе Sentry 2 каждый пользователь должен иметь уникальный адрес электронной почты.

Начало работы с Sentry 2

Лучше всего знакомиться с работой Sentry 2 на примере. Начнем изучение кода с простого – с аутентификации пользователя. Рассмотрим листинг 4, который иллюстрирует основной процесс.

Листинг 4. Аутентификация пользователя
<?php
// установка автозагрузчика
require ('vendor\autoload.php');

// настройка базы данных
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
  new PDO($dsn, $u, $p));
);
  
// указание учетных данных
$credentials = array(
  'email'    => 'vikram@example.com',
  'password' => 'guessme',
);

// аутентификация
try {
  $currentUser = Cartalyst\Sentry\Facades\Native\Sentry::
    authenticate($credentials, false);
  echo 'Logged in as ' . $currentUser->getLogin();
} catch (Exception $e) {
  echo 'Authentication error: ' . $e->getMessage();
}
?>

Листинг 4 начинается с загрузки сценария автозагрузчика Composer, который по мере необходимости "подтягивает" компоненты из Sentry 2. Затем с помощью фасадного метода setupDatabaseResolver() выполняется подключение системы Sentry 2 к созданной ранее базе данных MySQL путем передачи PDO-объекта с DSN и учетными данными базы данных. Это общие предварительные шаги для каждого использования Sentry 2.

После подключения к базе данных выполняется аутентификация с помощью метода authenticate(). Этот метод принимает массив, содержащий адрес электронной почты пользователя и пароль, и сверяет эту информацию с информацией в базе данных. Если учетные данные совпадают, создается новый объект User для представления пользователя, вошедшего в систему, а информация об этом пользователе сохраняется в сеансе. Теперь методы объекта User, такие как getLogin(), getPermissions(), isActivated() и другие, можно использовать при поиске конкретной информации о пользователе.

Вход и выход из системы

Имея эту информацию, можно быстро адаптировать листинг 4 для создания инфраструктуры входа/выхода для Web-приложения. В листинге 5 приведена традиционная форма входа в систему.

Листинг 5. Форма входа
<html>
<head></head>
<body> 
  <h1>Login</h2>
  <form action="login.php" method="post">
    Username: <input type="text" name="username" /> <br/>
    Password: <input type="password" name="password" /> <br/>
    <input type="submit" name="submit" value="Log In" />
  </form>
</body>
</html>

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

Листинг 6. Аутентификация пользователя и вход в систему
<?php
// установка автозагрузчика
require ('vendor\autoload.php');

// настройка базы данных
$dsn = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
  new PDO($dsn, $u, $p));
);

// проверка отправки формы
if (isset($_POST['submit'])) {
  try {
    // проверка входных данных
    $username = filter_var($_POST['username'], FILTER_SANITIZE_EMAIL);
    $password = strip_tags(trim($_POST['password']));
    
    // указание учетных данных
    $credentials = array(
      'email'    => $username,
      'password' => $password,
    );

    // аутентификация
    $currentUser = Cartalyst\Sentry\Facades\Native\Sentry::
      authenticate($credentials, false);
    echo 'Logged in as ' . $currentUser->getLogin(); 
  } catch (Exception $e) {
    echo 'Authentication error: ' . $e->getMessage();
  }
}
?>

Как правило, после входа пользователя в систему на посещаемых им страницах желательно отображать информацию о входе, а не форму входа. Поскольку после аутентификации Sentry 2 автоматически сохраняет объект User в сеансе, можно проверить его наличие и в зависимости от этого отображать либо форму входа, либо информацию о входе пользователя в систему. Рассмотрим листинг 7, в котором объединены листинги 5 и 6 и добавлена необходимая логика.

Листинг 7. Аутентификация пользователя с отображением формы входа по условию
<?php
// установка автозагрузчика
require ('vendor\autoload.php');

// настройка базы данных
$dsn      = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
  new PDO($dsn, $u, $p));

// настройка базы данных
if (isset($_POST['submit'])) {
  try {
    // проверка входных данных
    $username = filter_var($_POST['username'], FILTER_SANITIZE_EMAIL);
    $password = strip_tags(trim($_POST['password']));
    
    // указание учетных данных
    $credentials = array(
      'email'    => $username,
      'password' => $password,
    );

    // аутентификация
    // сообщение об ошибке, если аутентификация не выполнена
    Cartalyst\Sentry\Facades\Native\Sentry::authenticate($credentials, false);    
  } catch (Exception $e) {
    $failMessage = $e->getMessage();
  } 
}

// проверка входа пользователя в систему
if (Cartalyst\Sentry\Facades\Native\Sentry::check()) {
  $currentUser = Cartalyst\Sentry\Facades\Native\Sentry::getUser();
}
?>    
<html>
<head></head>
<body> 
  <?php if (isset($currentUser)): ?>
  Logged in as <?php echo $currentUser->getLogin(); ?>
  <a href="logout.php">[Log out]</a>
  <?php else: ?>
  <h1>Log In</h1>
  <div><?php echo (isset($failMessage)) ? 
    $failMessage : null; ?></div> 
  <form action="login.php" method="post">
    Username: <input type="text" name="username" /> <br/>
    Password: <input type="password" name="password" /> <br/>
    <input type="submit" name="submit" value="Log In" />
  </form>
  <?php endif; ?>
</body>
</html>

В листинге 7 используются два дополнительных метода Sentry 2: check() – вспомогательный (helper) метод проверки входа пользователя в систему, и getUser() – возвращает объект User, представляющий вошедшего в систему пользователя. Эти методы позволяют легко отобразить соответствующую информацию пользователя, основываясь на текущем статусе аутентификации.

Заключительная часть кода посвящена логике выходы из системы. Она очень проста. Достаточно вызвать метод logout(), чтобы был уничтожен объект User в сеансе и пользователь вышел из системы. В листинге 8 приведен пример кода.

Листинг 8. Деаутентификация и выход пользователя из системы
<?php
// установка автозагрузчика
require ('vendor\autoload.php');

// настройка базы данных
$dsn      = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
  new PDO($dsn, $u, $p));

// выход пользователя из системы
Cartalyst\Sentry\Facades\Native\Sentry::logout();
?>

Безопасное восстановление пароля

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

В составе Sentry 2 есть ряд методов, которые позволяют упростить этот процесс:

  • Метод getResetPasswordCode() генерирует для пользователя уникальный код восстановления пароля.
  • Метод checkResetPasswordCode() проверяет код восстановления пароля, предоставленный пользователем.
  • Метод attemptResetPassword() пытается заменить пароль пользователя новым значением после проверки кода восстановления пароля.

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

Листинг 9. Форма запроса восстановления пароля
<html>
<head></head>
<body> 
  <h1>Reset Password</h2>
  <form action="reset-request.php" method="post">
    Email address: <br/>
    <input type="text" name="email" /> <br/>
    <input type="submit" name="submit" value="Submit Request" />
  </form>
</body>
</html>

Адрес электронной почты, отправленный пользователем, попадает в PHP-сценарий, который инициализирует пакет Sentry 2, проверяет формат предоставленного адреса и пытается найти соответствующую запись о пользователе (см. листинг 10).

Листинг 10. Генерирование кода восстановления пароля
<?php 
if (isset($_POST['submit'])) {
  // установка автозагрузчика
  require ('vendor\autoload.php');

  // настройка базы данных
  $dsn      = 'mysql:dbname=appdata;host=localhost';
  $u = 'sentry';
  $p = 'g39ejdl';
  Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
    new PDO($dsn, $u, $p));
  
  // проверка входных данных и поиск записи о пользователе
  // отправка пользователю кода восстановления по электронной почте
  try {
    $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
    $user = Cartalyst\Sentry\Facades\Native\Sentry::findUserByCredentials(array(
      'email' => $email
    ));
    
    $code = $user->getResetPasswordCode();

    $subject = 'Your password reset code';
    $message = 'Code: ' . $code;
    $headers = 'From: webmaster@example.com';
    if (!mail($email, $subject, $message, $headers)) {
      throw new Exception('Email could not be sent.');
    }    
    
    echo 'Password reset code sent.';   
    exit;
  } catch (Exception $e) {
    echo $e->getMessage();
    exit;
  }
}
?>

Метод findUserByCredentials() в листинге 10 выполняет поиск в базе данных пользователей учетной записи, соответствующей предоставленным учетным данным и возвращает соответствующий объект User, если совпадение найдено. Метод getResetPasswordCode() объекта User используется для создания уникального кода восстановления, а функция mail() используется для отправки этого кода на электронную почту пользователя.

После получения кода восстановления пароля пользователь должен повторно посетить Web-приложение, отправить полученный код для подтверждения запроса и предоставить новый пароль. В листинге 11 приведен пример формы, которую увидит пользователь в данном случае.

Листинг 11. Форма восстановления пароля
<html>
<head></head>
<body> 
  <h1>Reset Password</h2>
  <form action="reset-password.php" method="post">
    Email address: <br/>
    <input type="text" name="email" /> <br/>
    Reset code: <br/>
    <input type="text" name="code" /> <br/>
    New password: <br/>
    <input type="password" name="password" /> <br/>
    New password (repeat): <br/>
    <input type="password" name="password-repeat" /> <br/>
    <input type="submit" name="submit" value="Change Password" />
  </form>
</body>
</html>

После отправки формы пользователем сценарий обработки сравнивает предоставленный код восстановления с кодом, записанным в базу данных пользователей, используя адрес электронной почты в качестве ключа. В случае совпадения пароль пользователя заменяется новым значением, предоставленным в форме. В листинге 12 приведена необходимая бизнес-логика, использующая встроенные методы Sentry 2.

Листинг 12. Подтверждение восстановления пароля
<?php 
if (isset($_POST['code']) & $_POST['email']) {

  // установка автозагрузчика
  require ('vendor\autoload.php');

  // настройка базы данных
  $dsn      = 'mysql:dbname=appdata;host=localhost';
  $u = 'sentry';
  $p = 'g39ejdl';
  Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
    new PDO($dsn, $u, $p));

  // поиск пользователя по адресу электронной почты
  // восстановление пароля
  try {
    $code = strip_tags($_POST['code']);
    $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
    $password = htmlentities($_POST['password']);
    $password_repeat = htmlentities($_POST['password-repeat']);
    if ($password != $password_repeat) {
      throw new Exception ('Passwords do not match.');
    }
    
    $user = Cartalyst\Sentry\Facades\Native\Sentry::findUserByCredentials(array(
      'email' => $email
    ));
    
    if ($user->checkResetPasswordCode($code)) {
      if ($user->attemptResetPassword($code, $password)) {
        echo 'Password successfully reset.';
        exit;
      } else {
        throw new Exception('User password could not be reset.');  
      }
    } else {
      throw new Exception('User password could not be reset.');  
    }
  } catch (Exception $e) {
    echo $e->getMessage();
    exit;
  }
}
?>

Листинг 12 начинается с установки объекта Sentry 2 и его подключения к базе данных. Затем выполняются проверка полученных с запросом входных переменных и их очистка, а также проверка формата адреса электронной почты и идентичности двух предоставленных паролей. После этих проверок сценарий с помощью метода findUserByCredentials()создает объект User, соответствующий предоставленному адресу электронной почты, а затем вызывает метод checkResetPasswordCode(), чтобы проверить достоверность предоставленного кода.

Если код, предоставленный пользователем, совпадает с записанным в базе данных, запрос восстановления пароля считается достоверным. В этом случае методу attemptResetPassword() объекта User передаются код восстановления и новый пароль, а он, в свою очередь, меняет пароль в базе данных пользователей.

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

Безопасная регистрация пользователей

В предыдущих примерах демонстрировалась аутентификация существующих пользователей. В то же время в большинстве Web-приложений, как правило, необходим интерфейс для создания новых пользователей. Здесь есть два варианта: пользователи регистрируют свои учетные записи самостоятельно или это делают за них администраторы (возможно совмещение обоих вариантов).

Sentry 2 предоставляет методы для безопасной реализации этих вариантов. Общим для обоих вариантов является метод createUser(), который принимает массив пар ключ-значение, а затем создает и сохраняет объект User, отображающий эту информацию в базу данных. В листинге 13 приведен пример.

Листинг 13. Создание учетной записи пользователя
<?php
// установка автозагрузчика
require ('vendor\autoload.php');

// настройка базы данных
$dsn      = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
  new PDO($dsn, $u, $p));

// создание записи о пользователе
try {

  $user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
      'email'    => 'test@example.com',
      'password' => 'guessme',
      'first_name' => 'Test',
      'last_name' => 'User',
      'activated' => true,
  ));
  
} catch (Exception $e) {
  echo $e->getMessage();
}
?>

Обратите внимание на ключ 'activated' в массиве, переданном в метод createUser(). Если значение этого ключа false, запись о пользователе сохраняется в базе данных, но учетная запись пользователя остается отключенной до прохождения проверки. Это добавляет весьма необходимый элемент безопасности при создании общедоступных форм регистрации, поскольку пользователь должен дополнительно активировать учетную запись, прежде чем сможет использовать приложение.

Посмотрим, как это работает на практике в примере формы регистрации (листинг 14).

Листинг 14. Учетная запись пользователя при самостоятельной регистрации
<?php 
if (isset($_POST['submit'])) {
  // установка автозагрузчика
  require ('vendor\autoload.php');

  // настройка базы данных
  $dsn      = 'mysql:dbname=appdata;host=localhost';
  $u = 'sentry';
  $p = 'g39ejdl';
  Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
    new PDO($dsn, $u, $p));
  
  // проверка входных данных и создание записи о пользователе
  // отправка пользователю кода активации по электронной почте
  try {
    $email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
    $fname = strip_tags($_POST['first_name']);
    $lname = strip_tags($_POST['last_name']);
    $password = strip_tags($_POST['password']);
    
    $user = Cartalyst\Sentry\Facades\Native\Sentry::createUser(array(
        'email'    => $email,
        'password' => $password,
        'first_name' => $fname,
        'last_name' => $lname,
        'activated' => false
    ));

    $code = $user->getActivationCode();
    
    $subject = 'Your activation code';
    $message = 'Code: ' . $code;
    $headers = 'From: webmaster@example.com';
    if (!mail($email, $subject, $message, $headers)) {
      throw new Exception('Email could not be sent.');
    }    
    
    echo 'User successfully registered and activation code sent.';   
    exit;
  } catch (Exception $e) {
    echo $e->getMessage();
    exit;
  }
}
?>
<html>
<head></head>
<body> 
  <h1>Register</h2>
  <form action="register.php" method="post">
    Email address: <br/>
    <input type="text" name="email" /> <br/>
    Password: <br/>
    <input type="password" name="password" /> <br/>
    First name: <br/>
    <input type="text" name="first_name" /> <br/>
    Last name: <br/>
    <input type="text" name="last_name" /> <br/>
    <input type="submit" name="submit" value="Create" />
  </form>
</body>
</html>

Листинг 14 создает форму с полями для ввода адреса электронной почты, пароля, имени и фамилии. Пользователь должен зарегистрироваться самостоятельно, заполнив и отправив форму. При отправке сценарий обработки проверяет входные данные (этот шаг в этом и других листингах преднамеренно сокращен), а затем вызывает метод createUser() для создания учетной записи пользователя без ее активации.

Следующим шагом является проверка информации, предоставленной пользователем. Это делается путем отправки уникального кода активации на предоставленный адрес электронной почты с просьбой к получателю подтвердить регистрацию, посетив сайт и введя этот код. Для получения кода активации используется метод getActivationCode() объекта User. Остается только отправить код на указанный адрес электронной почты.

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

Листинг 15 содержит необходимый код для страницы подтверждения в предположении, что эта ссылка имеет вид http://your-site.com/confirm.php?code=[code]&email=[email].

Листинг 15. Подтверждение учетной записи пользователя
<?php 
if (isset($_GET['code']) & $_GET['email']) {

  // установка автозагрузчика
  require ('vendor\autoload.php');

  // настройка базы данных
  $dsn      = 'mysql:dbname=appdata;host=localhost';
  $u = 'sentry';
  $p = 'g39ejdl';
  Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
    new PDO($dsn, $u, $p));

  // поиск пользователя по адресу электронной почты
  // активация пользователя с помощью кода активации
  try {
    $code = strip_tags($_GET['code']);
    $email = filter_var($_GET['email'], FILTER_SANITIZE_EMAIL);
    $user = Cartalyst\Sentry\Facades\Native\Sentry::findUserByCredentials(array(
      'email' => $email
    ));
    if ($user->attemptActivation($code)) {
      echo 'User activated.';
    } else {
      throw new Exception('User could not be activated.');  
    }
  } catch (Exception $e) {
    echo $e->getMessage();
  }
}
?>

В листинге 15 нет ничего сложного. Он проверяет входящий запрос на переменные 'code' и 'email' и при их наличии использует метод findUserByCredentials(), чтобы определить активируемую учетную запись. Затем, используя предоставленный код активации, он вызывает метод attemptActivation(). Если код активации соответствует коду в базе данных, учетная запись активируется, если же нет – попытка активации отклоняется, и учетная запись остается неактивной.

Конечно, если вы не хотите, чтобы пользователи проходили дополнительный этап активации учетной записи (например, если вы создаете интранет-приложение, доступное только для известных пользователей, или если учетные записи пользователей создаются только доверенными администраторами), можно в вызове метода createUser() присвоить переменной 'activated' значение true, и учетные записи пользователей будут создаваться как активные.

Поиск пользователей

В предыдущих листингах вы видели несколько примеров использования метода findUserByCredentials(), но это не единственный способ поиска пользователей.

  • Метод getUser() возвращает только что вошедшего в систему пользователя.
  • Метод findUserByLogin() возвращает пользователя с соответствующим адресом электронной почты.
  • Метод findUserByActivationCode() возвращает пользователя с соответствующим кодом активации.
  • Метод findUserByResetPasswordCode() возвращает пользователя с соответствующим кодом восстановления пароля.
  • Метод findAllUsers() возвращает список всех пользователей (активных и неактивных).
  • Метод findAllUsersWithAccess() возвращает список всех пользователей с указанными полномочиями.
  • Метод findAllUsersInGroup() возвращает список всех пользователей группы (группы и полномочия обсуждаются во второй части этой серии).

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

Рассмотрим использование Sentry 2 для ускорения разработки инструмента управления пользователями. Как правило, нужно реализовать четыре основные функции: добавление новых пользователей, получение списка существующих пользователей, редактирование существующих пользователей и удаление пользователей. Вы уже видели, что первую функцию легко позволяет реализовать метод createUser() (пример есть в архиве кода), поэтому сосредоточимся на других.

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

Листинг 16. Получение списка учетных записей пользователей
<?php
// установка автозагрузчика
require ('vendor\autoload.php');

// настройка базы данных
$dsn      = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
  new PDO($dsn, $u, $p));

// поиск всех пользователей
$users = Cartalyst\Sentry\Facades\Native\Sentry::findAllUsers();
?>
<html>
  <head></head>
  <body>
    <h1>Users</h1>
    <table border="1">
      <tr>
        <td>Email address</td>
        <td>First name</td>
        <td>Last name</td>
        <td>Status</td>
        <td>Last login</td>
      </tr>
    <?php foreach ($users as $u): ?>
    <?php $userArr = $u->toArray(); ?>
      <tr>
        <td><?php echo $userArr['email']; ?></td>
        <td><?php echo isset($userArr['first_name']) ? 
          $userArr['first_name'] : '-'; ?></td>
        <td><?php echo isset($userArr['last_name']) ? 
          $userArr['last_name'] : '-'; ?></td>
        <td><?php echo ($userArr['activated'] == 1) ? 
          'Active' : 'Inactive'; ?></td>
        <td><?php echo isset($userArr['last_login']) ? 
          $userArr['last_login'] : '-'; ?></td>
        <td><a href="edit.php?id=<?php echo $userArr['id']; ?>">
          Edit</a></td>
        <td><a href="delete.php?id=<?php echo $userArr['id']; ?>">
          Delete</a></td>
      </tr>
    <?php endforeach; ?>
    </table>
    <a href="create.php">Add new user</a>
  <body>
</html>

В листинге 16 для получения списка всех пользователей из базы данных Sentry 2 используется метод findAllUsers(). Теперь легко можно перебрать этот список, преобразовать каждый объект в массив с помощью метода toArray(), а затем отобразить соответствующую информацию таблицы. Обратите внимание, что каждая запись о пользователе сопровождается ссылками на функции редактирования и удаления, которые содержат уникальный идентификатор пользователя. Мы рассмотрим их позже, а пока посмотрим на рисунок 4, иллюстрирующий пример выходных данных из листинга 16.

Рисунок 4. Получение списка учетных записей пользователей
Получение списка учетных записей пользователей
Получение списка учетных записей пользователей

Редактирование и удаление учетных записей пользователей

Sentry 2 предоставляет встроенные методы для изменения и удаления учетных записей пользователей. Рассмотрим листинг 17, на который указывают ссылки 'delete' в листинге 16.

Листинг 17. Удаление учетной записи пользователя
<?php
if (isset($_GET['id'])) {
  // установка автозагрузчика
  require ('vendor\autoload.php');

  // настройка базы данных
  $dsn      = 'mysql:dbname=appdata;host=localhost';
  $u = 'sentry';
  $p = 'g39ejdl';
  Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
    new PDO($dsn, $u, $p));

  // поиск пользователя по идентификатору и удаление
  try {
    $id = strip_tags($_GET['id']);    
    $user = Cartalyst\Sentry\Facades\Native\Sentry::findUserById($id);
    $user->delete();
    echo 'User successfully deleted.';
  } catch (Exception $e) {
    echo 'User could not be deleted.';
  }
}    
?>

Листинг 17 получает идентификатор выбранного пользователя в качестве параметра запроса GET, находит соответствующую запись, используя метод findUserById(), а затем вызывает метод delete() объекта User, чтобы удалить запись о пользователе из базы данных.

Для внесения изменений в существующую запись о пользователе также полезен метод save(). Рассмотрим листинг 18, который использует его для создания простого инструмента редактирования для пользователей.

Листинг 18. Модификация учетной записи пользователя
<?php
// установка автозагрузчика
require ('vendor\autoload.php');

// настройка базы данных
$dsn      = 'mysql:dbname=appdata;host=localhost';
$u = 'sentry';
$p = 'g39ejdl';
Cartalyst\Sentry\Facades\Native\Sentry::setupDatabaseResolver(
  new PDO($dsn, $u, $p));

if (isset($_POST['submit'])) {

  try {
    $id = strip_tags($_POST['id']);    
    $user = Cartalyst\Sentry\Facades\Native\Sentry::findUserById($id);    
    $user->email = filter_var($_POST['email'], FILTER_SANITIZE_EMAIL);
    $user->first_name = strip_tags($_POST['first_name']);
    $user->last_name = strip_tags($_POST['last_name']);
    $user->password = strip_tags($_POST['password']);
    
    if ($user->save()) {
      echo 'User successfully updated.';
      exit;
    } else {
      throw new Exception('User could not be updated.');
    }
  } catch (Exception $e) {
    echo 'User could not be created.';
    exit;
  }

} else if (isset($_GET['id'])) {

  try {
    $id = strip_tags($_GET['id']);    
    $user = Cartalyst\Sentry\Facades\Native\Sentry::findUserById($id);
    $userArr = $user->toArray();
  } catch (Exception $e) {
    echo 'User could not be found.';
    exit;
  }
  
?>
<html>
<head></head>
<body> 
  <h1>Edit User</h2>
  <form action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>" 
    method="post">
    Email address: <br/>
    <input type="text" name="email" 
      value="<?php echo $userArr['email']; ?>" /> <br/>
    Password: <br/>
    <input type="password" name="password" 
      value="<?php echo $userArr['password']; ?>" /> <br/>
    First name: <br/>
    <input type="text" name="first_name" 
      value="<?php echo $userArr['first_name']; ?>" /> <br/>
    Last name: <br/>
    <input type="text" name="last_name" 
      value="<?php echo $userArr['last_name']; ?>" /> <br/>
    <input type="hidden" name="id" 
      value="<?php echo $userArr['id']; ?>" /> <br/>
    <input type="submit" name="submit" value="Update" />
  </form>
</body>
</html>
<?php 
}
?>

Аналогично листингу 17 листинг 18 также получает идентификатор пользователя из листинга 16. Затем он использует этот идентификатор для создания соответствующего объекта User, преобразования его в массив и извлечения необходимой информации для предварительного заполнения полей формы редактирования (включая скрытое поле для идентификатора пользователя). Пользователь может изменить эти поля, а затем отправить форму обратно для обработки.

Введенные пользователем входные данные очищаются и проверяются, а скрытый идентификатор пользователя используется для получения соответствующего объекта User. Новые значения, переданные через форму, устанавливаются как свойства объекта User, а затем вызывается метод save() этого объекта для сохранения новых значений в базе данных.

Заключение

Как показывают приведенные примеры, Sentry 2 предоставляет полнофункциональную инфраструктуру для управления и аутентификации пользователей в Web-приложении, написанном на PHP. Тем не менее все, что вы видели до сих пор, – это лишь верхушка айсберга: Sentry 2 также содержит функции для групп и полномочий, а также дополнительные функции безопасности, такие как запрет пользователей и временное блокирование входа.

Звучит интересно? Обратитесь ко второй (заключительной) части (EN) данной серии, которая посвящена этим и некоторым другим функциям. Увидимся!


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


Похожие темы


Комментарии

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Open source
ArticleID=999000
ArticleTitle=Sentry 2 и PHP: Часть 1. Аутентификация и управление доступом для PHP
publish-date=02272015